<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.5">
<title>Spring</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove comment around @import statement below when using as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
[hidden],template{display:none}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.spread{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.no-bullet{list-style:none}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite:before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
.clearfix:after,.float-group:after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
*:not(pre)>code.nobreak{word-wrap:normal}
*:not(pre)>code.nowrap{white-space:nowrap}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
b.button:before{content:"[";padding:0 3px 0 2px}
b.button:after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
#content{margin-top:1.25em}
#content:before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber:after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}
@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]:before{display:block}
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote:before{display:none}
.verseblock{margin:0 1em 1.25em 1em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot{border-width:1px 0}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{width:1em;position:relative;top:1px}
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
ul.inline>li>*{display:block}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@media print{@page{margin:1.25cm .75cm}
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
.sect1{padding-bottom:0!important}
.sect1+.sect1{border:0!important}
#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span:before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]:before{display:block}
#footer{background:none!important;padding:0 .9375em}
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
</head>
<body class="book">
<div id="header">
<h1>Spring</h1>
<div id="toc" class="toc">
<div id="toctitle">Spring</div>
<ul class="sectlevel1">
<li><a href="#_case">1. case</a></li>
<li><a href="#__webmvctest">2. @WebMvcTest</a></li>
<li><a href="#_示例代码">3. 示例代码</a></li>
<li><a href="#_1_maven_坐标">4. 1 maven 坐标</a></li>
<li><a href="#_2_客户端参数校验">5. 2 客户端参数校验</a>
<ul class="sectlevel2">
<li><a href="#_2_1_表单">5.1. 2.1 表单</a></li>
<li><a href="#_2_2_使用_validated">5.2. 2.2 使用 @Validated</a></li>
<li><a href="#_2_3_将异常统一处理">5.3. 2.3 将异常统一处理</a></li>
<li><a href="#_2_4_分组校验">5.4. 2.4 分组校验</a></li>
</ul>
</li>
<li><a href="#_1_新建springboot项目">6. 1. 新建springboot项目</a></li>
<li><a href="#_2_准备工作">7. 2. 准备工作</a></li>
<li><a href="#_3_src_main_java">8. 3. src/main/java</a></li>
<li><a href="#_4_src_main_test">9. 4. src/main/test</a>
<ul class="sectlevel2">
<li><a href="#_4_1_配置_spring_rest_doc_输出以及处理方式">9.1. 4.1 配置 Spring Rest Doc 输出以及处理方式</a></li>
<li><a href="#_4_2_编写测试_并生成文档">9.2. 4.2 编写测试，并生成文档</a></li>
</ul>
</li>
<li><a href="#_5_运行测试_并生成结果">10. 5. 运行测试，并生成结果</a></li>
<li><a href="#_6_编写测试文档adoc_并生成对应的html文件">11. 6 编写测试文档adoc,并生成对应的html文件</a></li>
<li><a href="#_6_补充说明">12. 6. 补充说明</a></li>
<li><a href="#_7_附表">13. 7. 附表</a></li>
<li><a href="#_1_概览总结">14. 1 概览总结</a>
<ul class="sectlevel2">
<li><a href="#_1_1_核心实现包">14.1. 1.1 核心实现包：</a></li>
<li><a href="#_1_2_核心对象">14.2. 1.2 核心对象：</a></li>
</ul>
</li>
<li><a href="#_2_容器">15. 2 容器</a>
<ul class="sectlevel2">
<li><a href="#_2_1_元信息">15.1. 2.1 元信息</a></li>
<li><a href="#_2_2_创建容器">15.2. 2.2 创建容器</a></li>
<li><a href="#_2_3_使用容器">15.3. 2.3 使用容器</a></li>
<li><a href="#_2_4_bean_对象">15.4. 2.4 Bean 对象</a></li>
<li><a href="#_2_5_初始化_bean_的多种方式">15.5. 2.5 初始化 bean 的多种方式</a></li>
</ul>
</li>
<li><a href="#_3_依赖">16. 3 依赖</a></li>
<li><a href="#_4_bean_的scopes">17. 4 bean 的scopes</a></li>
<li><a href="#_5_自定义_bean_nature">18. 5 自定义 bean nature</a>
<ul class="sectlevel2">
<li><a href="#_5_1_生命周期回调">18.1. 5.1 生命周期回调</a></li>
<li><a href="#_5_2_启动_和_关闭_回调">18.2. 5.2 启动 和 关闭 回调</a></li>
<li><a href="#_5_3_非web应用_关闭_ioc_容器时回调">18.3. 5.3 非web应用，关闭 IOC 容器时回调</a></li>
<li><a href="#_5_4_aware">18.4. 5.4 Aware</a></li>
</ul>
</li>
<li><a href="#_6_bean_定义继承">19. 6 Bean 定义继承</a></li>
<li><a href="#_7_容器扩展方法">20. 7 容器扩展方法</a></li>
<li><a href="#_8_基于注解的容器配置">21. 8 基于注解的容器配置</a>
<ul class="sectlevel2">
<li><a href="#_8_1_required">21.1. 8.1 @Required</a></li>
<li><a href="#_8_2_autowired">21.2. 8.2 @Autowired</a></li>
<li><a href="#_8_3_primary">21.3. 8.3 @Primary</a></li>
<li><a href="#_8_4_qualifier">21.4. 8.4 @Qualifier</a></li>
<li><a href="#_8_5_value">21.5. 8.5 @Value</a></li>
</ul>
</li>
<li><a href="#_9_classpath_扫描以及管理_components">22. 9 classpath 扫描以及管理 components</a>
<ul class="sectlevel2">
<li><a href="#_9_1_常见注解">22.1. 9.1 常见注解</a></li>
<li><a href="#_9_2_元注解和组合注解">22.2. 9.2 元注解和组合注解</a></li>
<li><a href="#_9_3_扫描">22.3. 9.3 扫描</a></li>
<li><a href="#_9_4_过滤某些类">22.4. 9.4 过滤某些类。</a></li>
<li><a href="#_9_5_使用compoent_定义_bean_的元信息">22.5. 9.5 使用compoent 定义 Bean 的元信息</a></li>
<li><a href="#_9_6_bean_命名">22.6. 9.6 Bean 命名</a></li>
<li><a href="#_9_7_使用_scope_指定_自动检测_components">22.7. 9.7 使用 scope 指定 自动检测 components</a></li>
</ul>
</li>
<li><a href="#_10_使用_jsr_330_标准注解">23. 10 使用 JSR 330 标准注解。</a></li>
<li><a href="#_11_基于java代码_进行容器配置">24. 11 基于java代码，进行容器配置</a>
<ul class="sectlevel2">
<li><a href="#_11_1_import">24.1. 11.1 @Import</a></li>
<li><a href="#_11_2_环境设置">24.2. 11.2 环境设置</a></li>
<li><a href="#_11_3_spring_中的事件机制">24.3. 11.3 spring 中的事件机制</a></li>
</ul>
</li>
<li><a href="#_1_基本java表达式计算">25. 1 基本java表达式计算</a>
<ul class="sectlevel2">
<li><a href="#_1_1_核心类">25.1. 1.1 核心类</a></li>
<li><a href="#_1_2_使用方式">25.2. 1.2 使用方式</a></li>
</ul>
</li>
<li><a href="#_2_evaluationcontext">26. 2 EvaluationContext</a>
<ul class="sectlevel2">
<li><a href="#_2_1_类型转换">26.1. 2.1 类型转换</a></li>
</ul>
</li>
<li><a href="#_3_解析配置">27. 3 解析配置</a></li>
<li><a href="#_4_编译_compiler_vs_解释_interpreter">28. 4 编译 compiler vs 解释 interpreter</a>
<ul class="sectlevel2">
<li><a href="#_4_1_编译模式有3中选项">28.1. 4.1 编译模式有3中选项：</a></li>
<li><a href="#_4_2_示例">28.2. 4.2 示例</a></li>
<li><a href="#_4_3_第二种设置_compiler_方式">28.3. 4.3 第二种设置 compiler 方式</a></li>
<li><a href="#_4_4_complier_限制">28.4. 4.4 complier 限制</a></li>
</ul>
</li>
<li><a href="#_5_在定义中使用表达式">29. 5 在定义中使用表达式</a></li>
<li><a href="#_6_常见的表达式使用">30. 6 常见的表达式使用</a>
<ul class="sectlevel2">
<li><a href="#_6_1_字面量表达式">30.1. 6.1 字面量表达式</a></li>
<li><a href="#_6_2_属性_数组_集合表达式">30.2. 6.2 属性、数组、集合表达式</a></li>
<li><a href="#_6_3_其他总结下_具体看官网">30.3. 6.3 其他总结下，具体看官网</a></li>
</ul>
</li>
<li><a href="#_1_maven坐标">31. 1. maven坐标</a></li>
<li><a href="#_2_启用_enablecaching">32. 2. 启用 @EnableCaching</a></li>
<li><a href="#_3_注解功能">33. 3. 注解功能</a></li>
<li><a href="#_4_cacheable_缓存数据">34. 4. @Cacheable 缓存数据。</a>
<ul class="sectlevel2">
<li><a href="#_4_1_源码">34.1. 4.1 源码</a></li>
<li><a href="#_4_3_使用">34.2. 4.3 使用</a></li>
</ul>
</li>
<li><a href="#_5_cacheevict_清除缓存">35. 5 @CacheEvict 清除缓存</a>
<ul class="sectlevel2">
<li><a href="#_5_1_源码">35.1. 5.1 源码</a></li>
</ul>
</li>
<li><a href="#_6_cacheput">36. 6 @CachePut</a>
<ul class="sectlevel2">
<li><a href="#_6_2_源码">36.1. 6.2 源码</a></li>
</ul>
</li>
<li><a href="#_7_caching">37. 7 @Caching</a>
<ul class="sectlevel2">
<li><a href="#_7_2_源码">37.1. 7.2 源码</a></li>
</ul>
</li>
<li><a href="#_8_cacheconfig">38. 8 @CacheConfig</a>
<ul class="sectlevel2">
<li><a href="#_8_1_源码">38.1. 8.1 源码</a></li>
</ul>
</li>
<li><a href="#_需求">39. 需求</a></li>
<li><a href="#_方法">40. 方法</a></li>
<li><a href="#_1_创建mockmvc_对象">41. 1 创建MockMvc 对象</a></li>
<li><a href="#_2_发起请求">42. 2 发起请求</a>
<ul class="sectlevel2">
<li><a href="#_2_1_构建请求的工具类">42.1. 2.1 构建请求的工具类</a></li>
<li><a href="#_2_2_一次完整的http请求以及处理流程">42.2. 2.2 一次完整的http请求以及处理流程</a></li>
<li><a href="#_2_3_mockmvcrequestbuilders">42.3. 2.3 MockMvcRequestBuilders</a></li>
<li><a href="#_2_4_mockmvcresultmatchers">42.4. 2.4 MockMvcResultMatchers</a></li>
<li><a href="#_2_5_mockmvcresulthandlers">42.5. 2.5 MockMvcResultHandlers</a></li>
</ul>
</li>
<li><a href="#_3_总结">43. 3 总结</a></li>
<li><a href="#_静态包">44. 静态包</a></li>
<li><a href="#_类">45. 类</a></li>
<li><a href="#_mock以及初始化">46. mock以及初始化</a></li>
<li><a href="#_环境">47. 环境</a></li>
<li><a href="#_乱码问题">48. 乱码问题</a></li>
<li><a href="#_原因">49. 原因</a></li>
<li><a href="#_解决办法">50. 解决办法</a>
<ul class="sectlevel2">
<li><a href="#_方式一_将请求后得到的response_修改其编码值">50.1. 方式一：将请求后得到的response，修改其编码值</a></li>
<li><a href="#_方式二_全局拦截_推荐">50.2. 方式二：全局拦截 (推荐)</a></li>
</ul>
</li>
<li><a href="#_mockito_中文文档_2_0_26_beta">51. Mockito 中文文档 ( 2.0.26 beta )</a>
<ul class="sectlevel2">
<li><a href="#_参与人员">51.1. 参与人员</a></li>
<li><a href="#_目录">51.2. 目录</a></li>
<li><a href="#_0_迁移到mockito_2_0">51.3. 0. 迁移到Mockito 2.0</a></li>
<li><a href="#_1_验证某些行为">51.4. 1. 验证某些行为</a></li>
<li><a href="#_2_link_如何做一些测试桩_stub">51.5. 2. link:[如何做一些测试桩 (Stub)]</a></li>
<li><a href="#_3_link_参数匹配器_matchers">51.6. 3. link:[参数匹配器 (matchers)]</a></li>
<li><a href="#_4_link_验证函数的确切_最少_从未调用次数">51.7. 4. link:[验证函数的确切、最少、从未调用次数]</a></li>
<li><a href="#_5_link_为返回值为void的函数通过stub抛出异常">51.8. 5. link:[为返回值为void的函数通过Stub抛出异常]</a></li>
<li><a href="#_6_link_验证执行执行顺序">51.9. 6. link:[验证执行执行顺序]</a></li>
<li><a href="#_7_link_确保交互_interaction_操作不会执行在mock对象上">51.10. 7. link:[确保交互(interaction)操作不会执行在mock对象上]</a></li>
<li><a href="#_8_link_查找冗余的调用">51.11. 8. link:[查找冗余的调用]</a></li>
<li><a href="#_9_link_简化mock对象的创建">51.12. 9. link:[简化mock对象的创建]</a></li>
<li><a href="#_10_link_为连续的调用做测试桩_stub">51.13. 10. link:[为连续的调用做测试桩 (stub)]</a></li>
<li><a href="#_11_link_为回调做测试桩">51.14. 11. link:[为回调做测试桩]</a></li>
<li><a href="#_12">51.15. 12.</a></li>
<li><a href="#_13_link_监控真实对象">51.16. 13. link:[监控真实对象]</a></li>
<li><a href="#_14_link_修改没有测试桩的调用的默认返回值_1_7版本之后">51.17. 14. link:[修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )]</a></li>
<li><a href="#_15_为下一步的断言捕获参数_1_8版本之后">51.18. 15. 为下一步的断言捕获参数 (1.8版本之后)</a></li>
</ul>
</li>
<li><a href="#_方法详情">52. 方法详情</a>
<ul class="sectlevel2">
<li><a href="#_after">52.1. after</a></li>
<li><a href="#_atleast">52.2. atLeast</a></li>
<li><a href="#_atleastonce">52.3. atLeastOnce</a></li>
<li><a href="#_atmost">52.4. atMost</a></li>
<li><a href="#_calls">52.5. calls</a></li>
</ul>
</li>
<li><a href="#_继承org_mockito_matchers的方法">53. 继承org.mockito.Matchers的方法</a>
<ul class="sectlevel2">
<li><a href="#_any">53.1. any</a></li>
<li><a href="#_any_2">53.2. any</a></li>
<li><a href="#_anybyte">53.3. anyByte</a></li>
<li><a href="#_anychar">53.4. anyChar</a></li>
<li><a href="#_anycollection">53.5. anyCollection</a></li>
<li><a href="#_anycollectionof">53.6. anyCollectionOf</a></li>
<li><a href="#_anydouble">53.7. anyDouble</a></li>
<li><a href="#_anyfloat">53.8. anyFloat</a></li>
<li><a href="#_anyint">53.9. anyInt</a></li>
<li><a href="#_anylist">53.10. anyList</a></li>
<li><a href="#_anylistof">53.11. anyListOf</a></li>
<li><a href="#_anylong">53.12. anyLong</a></li>
<li><a href="#_anymap">53.13. anyMap</a></li>
<li><a href="#_anymapof">53.14. anyMapOf</a></li>
<li><a href="#_anyobject">53.15. anyObject</a></li>
<li><a href="#_anyset">53.16. anySet</a></li>
<li><a href="#_anysetof">53.17. anySetOf</a></li>
<li><a href="#_anyshort">53.18. anyShort</a></li>
<li><a href="#_anystring">53.19. anyString</a></li>
<li><a href="#_anyvararg">53.20. anyVararg</a></li>
<li><a href="#_argthat">53.21. argThat</a></li>
<li><a href="#_booleanthat">53.22. booleanThat</a></li>
<li><a href="#_bytethat">53.23. byteThat</a></li>
<li><a href="#_charthat">53.24. charThat</a></li>
<li><a href="#_contains">53.25. contains</a></li>
<li><a href="#_description函数">53.26. description函数</a></li>
<li><a href="#_doanswer函数">53.27. doAnswer函数</a></li>
<li><a href="#_docallrealmethod函数">53.28. doCallRealMethod函数</a></li>
<li><a href="#_donothing函数">53.29. doNothing函数</a></li>
<li><a href="#_doreturn函数">53.30. doReturn函数</a></li>
<li><a href="#_dothrow函数">53.31. doThrow函数</a></li>
<li><a href="#_dothrow函数_2">53.32. doThrow函数</a></li>
<li><a href="#_ignorestubs函数">53.33. ignoreStubs函数</a></li>
<li><a href="#_inorder函数">53.34. inOrder函数</a></li>
<li><a href="#_mock函数">53.35. mock函数</a></li>
<li><a href="#_mock函数_2">53.36. mock函数</a></li>
<li><a href="#_mock函数_3">53.37. mock函数</a></li>
<li><a href="#_mock">53.38. mock</a></li>
<li><a href="#_mock_2">53.39. mock</a></li>
<li><a href="#_mockingdetails函数">53.40. mockingDetails函数</a></li>
</ul>
</li>
<li><a href="#_n_w开头的函数">54. n-w开头的函数</a>
<ul class="sectlevel2">
<li><a href="#_never_函数">54.1. never()函数</a></li>
<li><a href="#_only_函数">54.2. only()函数</a></li>
<li><a href="#_reset_t_mocks_函数">54.3. reset(T… mocks)函数</a></li>
<li><a href="#_spy_class_classtospy_函数">54.4. spy(Class classToSpy)函数</a></li>
<li><a href="#_stub_t_methodcall_函数">54.5. stub(T methodCall)函数</a></li>
<li><a href="#_stubvoid_t_mock_函数">54.6. stubVoid(T mock)函数</a></li>
<li><a href="#_timesout_long_millis_函数">54.7. timesout(long millis)函数</a></li>
<li><a href="#_time_int_wantednumberofinvocations_函数">54.8. time(int wantedNumberOfInvocations)函数</a></li>
<li><a href="#_validatemockitousage_函数">54.9. validateMockitoUsage()函数</a></li>
<li><a href="#_verify_t_mock_函数">54.10. verify(T mock)函数</a></li>
<li><a href="#_verifynomoreinteractions_object_mocks_函数">54.11. verifyNoMoreInteractions(Object… mocks)函数</a></li>
<li><a href="#_verifyzerointeractions_object_mocks_函数">54.12. verifyZeroInteractions(Object… mocks)函数</a></li>
<li><a href="#_when_t_methodcall_函数">54.13. when(T methodCall)函数</a></li>
<li><a href="#_withsettings_函数">54.14. withSettings()函数</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="_case">1. case</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.jianshu.com/p/4a8326d89991" class="bare">https://www.jianshu.com/p/4a8326d89991</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__webmvctest">2. @WebMvcTest</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@WebMvcTest(controllers = UserController.class)</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>这个注解仅用于Controller层的单元测试。默认情况下会仅实例化所有的Controller，可以通过指定单个Controller的方式实现对单个Controller的测试。</p>
</li>
<li>
<p>同时，如果被测试的Controller依赖Service的话，需要对该Service进行mock,如使用@MockBean</p>
</li>
<li>
<p>该注解的定义中还包括了@AutoConfigureMockMvc注解，因此，可以直接使用MockMvc对被测controller发起http请求。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_示例代码">3. 示例代码</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@WebMvcTest(controllers = UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void register() throws Exception {
        UserVO userVO = new UserVO();
        userVO.setAccount("MyAccount");
        userVO.setNickName("MyName");
        userVO.setAge(10);
        userVO.setPassword("my_password");
        userVO.setSex(1);
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                .post(UserController.URL_REGISTER)
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(userVO));
        ResultActions perform = mockMvc.perform(request);
        perform.andReturn().getResponse().setCharacterEncoding("UTF-8");
        perform.andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(print());
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation</a></p>
</div>
<div class="paragraph">
<p><a href="https://bestzuo.cn/posts/spring-validation.html" class="bare">https://bestzuo.cn/posts/spring-validation.html</a></p>
</div>
<div class="paragraph">
<p>spring 框架实现了一套validation，底层采用hibernate-validator。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_maven_坐标">4. 1 maven 坐标</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
    &lt;groupId&gt;org.hibernate.validator&lt;/groupId&gt;
    &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_客户端参数校验">5. 2 客户端参数校验</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_2_1_表单">5.1. 2.1 表单</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">class TestVO {
    @NotNull
    private String name;

    @NotNull
    private String password;

    @NotNull
    private String email;

    // getter setter
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_2_2_使用_validated">5.2. 2.2 使用 @Validated</h3>
<div class="paragraph">
<p><strong>方式一：</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@PostMapping("/validation")
public boolean learnValidator(@Validated TestVO vo) {
    return true;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>此方式TestVO如果不符合注解标注的，会抛出异常。</p>
</div>
<div class="paragraph">
<p><strong>方式二：</strong></p>
</div>
<div class="literalblock">
<div class="content">
<pre>@PostMapping("/validation")
public boolean learnValidator(@Validated TestVO vo, BindingResult result) {
    // result 异常自己可以处理。
    return true;
}</pre>
</div>
</div>
<div class="paragraph">
<p>此方式不会抛出异常，会将异常信息存放在BindResult中，可以对BindResult进行解析，进行异常处理。</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_3_将异常统一处理">5.3. 2.3 将异常统一处理</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@RestControllerAdvice
public class GlobalControllerAdvice {
    private static final String BAD_REQUEST_MSG = "客户端请求参数错误";
    // &lt;1&gt; 处理 form data方式调用接口校验失败抛出的异常
    @ExceptionHandler(BindException.class)
    public ResultInfo bindExceptionHandler(BindException e) {
        List&lt;FieldError&gt; fieldErrors = e.getBindingResult().getFieldErrors();
        List&lt;String&gt; collect = fieldErrors.stream()
                .map(o -&gt; o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // &lt;2&gt; 处理 json 请求体调用接口校验失败抛出的异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List&lt;FieldError&gt; fieldErrors = e.getBindingResult().getFieldErrors();
        List&lt;String&gt; collect = fieldErrors.stream()
                .map(o -&gt; o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // &lt;3&gt; 处理单个参数校验失败抛出的异常
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultInfo constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set&lt;ConstraintViolation&lt;?&gt;&gt; constraintViolations = e.getConstraintViolations();
        List&lt;String&gt; collect = constraintViolations.stream()
                .map(o -&gt; o.getMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_2_4_分组校验">5.4. 2.4 分组校验</h3>
<div class="paragraph">
<p>如果同一个参数，需要在不同场景下应用不同的校验规则，就需要用到分组校验了。比如：新注册用户还没起名字，我们允许
name 字段为空，但是不允许将名字更新为空字符。</p>
</div>
<div class="paragraph">
<p>分组校验有三个步骤：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>定义一个分组类（或接口）</p>
</li>
<li>
<p>在校验注解上添加groups属性指定分组</p>
</li>
<li>
<p>Controller 方法的 @Validated 注解添加分组类</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public interface Update extends Default{
}

public class UserVO {
    @NotBlank(message = "name 不能为空",groups = Update.class)
    private String name;
    // 省略其他代码...
}

@PostMapping("update")
public ResultInfo update(@Validated({Update.class}) UserVO userVO) {
    return new ResultInfo().success(userVO);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>所有不加分组标识的，都默认是Default.class。
如果校验的实体里还有对象，可以加@Valid，进行递归校验。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>官方教程：
<a href="https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#introduction" class="bare">https://docs.spring.io/spring-restdocs/docs/2.0.5.RELEASE/reference/html5/#introduction</a></p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>大致流程：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>使用spring-rest-doc结合test编写测试用例，可以是继承测试，也可以是只针对controller层。</p>
</li>
<li>
<p>运行test，即可生成对应的描述文档（Snippets）</p>
</li>
<li>
<p>手动编写adoc文件，将生成的snippets文档进行汇总(include)、排版</p>
</li>
<li>
<p>通过asccidocter-maven-plugin将手动编写的文档进行渲染，html等。(也可使用idea
插件生成html)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>注意：</strong>图片和引用文件的位置一定要正确设置，不然渲染不出内容。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_新建springboot项目">6. 1. 新建springboot项目</h2>
<div class="sectionbody">
<div class="paragraph">
<p>推荐使用spring 提供的
<a href="https://start.spring.io/">生成器</a>，可以避免很多坑，把需要的依赖及版本都配置好了，效果如下图。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/08/%E5%9B%BE%E7%89%87-672c5531914f4023bb8d31a3c6da2fb5.png" alt="图片.png">
</div>
<div class="title">Figure 1. 图片.png</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_准备工作">7. 2. 准备工作</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>需求：</strong> 一个用户注册接口，包含昵称、账号以及密码，简单即可。</p>
</div>
<div class="paragraph">
<p><strong>实现：</strong> 这里只做controller，模拟返回成功，service以及数据库就忽略。</p>
</div>
<div class="paragraph">
<p><strong>编辑器：</strong> IntelliJ IDEA 2021.1.2 (Community Edition)</p>
</div>
<div class="paragraph">
<p><strong>JDK:</strong> 1.8</p>
</div>
<div class="paragraph">
<p><strong>OS:</strong> windows 10</p>
</div>
<div class="paragraph">
<p><strong>lombok：</strong> 一个插件，可以通过注解生成getter、setter等，不需要深入研究。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_src_main_java">8. 3. src/main/java</h2>
<div class="sectionbody">
<div class="paragraph">
<p>新建一个UserController、UserVO、UserService，如下：</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/08/%E5%9B%BE%E7%89%87-c698cfdaf1144e1d86c72db9282eeacf.png" alt="图片.png">
</div>
<div class="title">Figure 2. 图片.png</div>
</div>
<div class="paragraph">
<p><strong>UerVO</strong>: 用户注册参数</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.learn.springrestdoc.module.user.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 用户注册参数
 *
 * @author wanghengzhi
 * @since 2021/8/7 8:55
 */
package com.wanghengzhi.learn.springrestdoc.module.user.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 用户注册参数
 *
 * @author wanghengzhi
 * @since 2021/8/7 8:55
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVO {
    /**
     * 昵称
     */
    private String nickName;
    /**
     * 账号
     */
    private String account;
    /**
     * 密码
     */
    private String password;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>UserController</strong>：加入一个注册接口</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.learn.springrestdoc.module.user;

import com.wanghengzhi.learn.springrestdoc.module.user.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用户操作接口
 *
 * @author wanghengzhi
 * @since 2021/8/7 8:54
 */
@RestController
@SuppressWarnings("unused")
public class UserController {

    public static final String URL_USER_REGISTER = "/user/register";

    @Autowired
    private UserService userService;

    /**
     * 用户注册接口
     * @param userVO 用户输入参数
     * @return 注册结果
     */
    @PostMapping(URL_USER_REGISTER)
    public ResponseEntity&lt;Object&gt; register(@RequestBody UserVO userVO) {
        // 调用 service 进行保存
        userService.register(userVO);
        return new ResponseEntity&lt;&gt;(HttpStatus.OK);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>UserService</strong>： 业务处理类，此例中不需要实现。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.learn.springrestdoc.module.user;

import com.wanghengzhi.learn.springrestdoc.module.user.vo.UserVO;
import org.springframework.stereotype.Service;

/**
 * 用户操作 service 层
 *
 * @author wanghengzhi
 * @since 2021/8/7 9:01
 */
@Service
public class UserService {

    @SuppressWarnings("unused")
    public void register(UserVO userVO) {
        // 省略业务逻辑。
    }

}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_4_src_main_test">9. 4. src/main/test</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/08/%E5%9B%BE%E7%89%87-5dee692bd4bd4e429fbb311c9b4f2463.png" alt="图片.png">
</div>
<div class="title">Figure 3. 图片.png</div>
</div>
<div class="paragraph">
<p>只需要新增一个`UserControllerTest`</p>
</div>
<div class="sect2">
<h3 id="_4_1_配置_spring_rest_doc_输出以及处理方式">9.1. 4.1 配置 Spring Rest Doc 输出以及处理方式</h3>
<div class="paragraph">
<p><code>MyRestDocsConfiguration</code>：
这里配置文档格式化输出，其他配置，可自行研究。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.tdd.config;

import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.boot.test.context.TestConfiguration;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;

/**
 * 配置 spring rest doc
 * @author wanghengzhi
 * @since 2021/8/6 23:02
 */
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {

    @Override
    public void customize(MockMvcRestDocumentationConfigurer configurer) {
        configurer.operationPreprocessors().withRequestDefaults(prettyPrint()).withResponseDefaults(prettyPrint());
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>MyResultHandlerConfiguration</code>: 配置了生成的文件名格式。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.learn.springrestdoc.config;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;

/**
 * 配置文档处理
 * @author wanghengzhi
 * @since 2021/8/6 23:03
 */
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {

    @Bean
    public RestDocumentationResultHandler restDocumentation() {
        return MockMvcRestDocumentation.document("{class_name}/{method_name}");
    }

}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_4_2_编写测试_并生成文档">9.2. 4.2 编写测试，并生成文档</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.learn.springrestdoc.module.user;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanghengzhi.learn.springrestdoc.config.MyRestDocsConfiguration;
import com.wanghengzhi.learn.springrestdoc.config.MyResultHandlerConfiguration;
import com.wanghengzhi.learn.springrestdoc.module.user.vo.UserVO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.restdocs.headers.HeaderDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.payload.PayloadDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 测试
 *
 * Annotation {@code @WebMvcTest} ：
 *      只测是Controller层代码，service层依赖需要使用{@code @MockBean}注入。
 * Annotation {@code @AutoConfigureRestDocs}：
 *      这里配置了生成snippets位置，默认放在target目录下，由于每次clean会删除。
 *      手动编写的*。adoc文档会找不到，会报错。如果多人开发，别人拉取代码，如果不运行test，编辑器会提示错误。
 * Annotation {@code @Import}：
 *      手动导入自定义的配置文件。包含文档生成规则配置，以及生成一个handler来处理文档。
 *
 * @author wanghengzhi
 * @since 2021.8.7
 */
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs(outputDir = "src/main/asciidoc/snippets")
@Import({MyRestDocsConfiguration.class, MyResultHandlerConfiguration.class})
class UserControllerTest {

    @Autowired
    private ObjectMapper objectMapper;
    @MockBean
    private UserService userService;
    @Autowired
    private MockMvc mockMvc;
    /**
     * 统一使用这个handler生成文档。
     */
    @Autowired
    private RestDocumentationResultHandler resultHandler;

    @Test
    void register() throws Exception {
        UserVO userVO = new UserVO();
        userVO.setAccount("my_account");
        userVO.setNickName("my_nickName");
        userVO.setPassword("my_password");
        String token = "My Header";
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post(UserController.URL_USER_REGISTER)
                .accept(MediaType.APPLICATION_JSON)
                .header(token, "My_Header_value")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(userVO));

        ResultActions perform = mockMvc.perform(request);
        perform.andReturn().getResponse().setCharacterEncoding("utf-8");

        perform.andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
        .andDo(resultHandler.document(
                HeaderDocumentation.requestHeaders(
                        HeaderDocumentation.headerWithName(token).description("自定义header").optional()
                ),
                PayloadDocumentation.requestFields(
                        PayloadDocumentation.fieldWithPath("nickName").type(JsonFieldType.STRING).description("用户昵称"),
                        PayloadDocumentation.fieldWithPath("account").type(JsonFieldType.STRING).description("用户账号"),
                        PayloadDocumentation.fieldWithPath("password").type(JsonFieldType.STRING).description("密码")
                ),
        PayloadDocumentation.responseFields(
                        PayloadDocumentation.fieldWithPath("ok").type(JsonFieldType.BOOLEAN).description("是否成功"),
                        PayloadDocumentation.fieldWithPath("message").type(JsonFieldType.STRING).description("信息")
                )));
    }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_5_运行测试_并生成结果">10. 5. 运行测试，并生成结果</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/08/%E5%9B%BE%E7%89%87-59cac1524eda459695341f61f3abc001.png" alt="图片.png">
</div>
<div class="title">Figure 4. 图片.png</div>
</div>
<div class="paragraph">
<p>红箭头标识的是，是测试类手动定义生成的，其他都是默认生成的文件。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_6_编写测试文档adoc_并生成对应的html文件">11. 6 编写测试文档adoc,并生成对应的html文件</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>新建 index.adoc，使用
<a href="https://docs.asciidoctor.org/asciidoc/latest/">ascciidoctor语法</a>。</p>
</li>
<li>
<p>根据需求格式进行排版，生成的snippets文件引用。</p>
</li>
<li>
<p>IDea有插件可以进行预览，也可以生成html</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/08/%E5%9B%BE%E7%89%87-2c44b888af2a463aadcdbc49ad92e752.png" alt="图片.png">
</div>
<div class="title">Figure 5. 图片.png</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_6_补充说明">12. 6. 补充说明</h2>
<div class="sectionbody">
<div class="paragraph">
<p>可以通过配置asciidoc-plugin，在指定maven目标中可以生成html。</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/asciidoctor/asciidoctor-maven-plugin/blob/main/README_zh-CN.adoc">官方配置说明</a></p>
</div>
<div class="paragraph">
<p>备注： 1. <code>&lt;version&gt;1.5.8&lt;/version&gt;</code>: 可以根据喜好选择版本。 2.
<code>&lt;executions&gt;...&lt;/executions&gt;</code>: 执行配置，
可多个，用于不同条件下的执行内容。 3. <code>&lt;execution&gt;...&lt;/execution&gt;</code>:
具体执行某个行为 4. <code>&lt;id&gt;generate-docs&lt;/id&gt;</code>：唯一即可，可自由命名 5.
<code>&lt;phase&gt;prepare-package&lt;/phase&gt;</code>：前置条件：在maven package前执行 6.
<code>&lt;goal&gt;process-asciidoc&lt;/goal&gt;</code>：
目的，可设置多个，这里执行process-asciidoc（处理.adoc文件为html5）。这是asciidoc插件的命令。见下图。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://www.wanghengzhi.com/upload/2021/05/%E5%9B%BE%E7%89%87-bd1125f63b6a42e59dc8407a3356fa12.png" alt="图片.png">
</div>
<div class="title">Figure 6. 图片.png</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_7_附表">13. 7. 附表</h2>
<div class="sectionbody">
<div class="paragraph">
<p>document：</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 34%;">
<col style="width: 33%;">
<col style="width: 33%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">关键字</th>
<th class="tableblock halign-left valign-top">作用</th>
<th class="tableblock halign-left valign-top">类名</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HeaderDocumentation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">记录header中的参数</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">org.springframework.restdocs.headers.HeaderDocumentation</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">PayloadDocumentation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">记录body中的参数</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">org.springframework.restdocs.payload.PayloadDocumentation</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">RequestDocumentation</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">org.springframework.restdocs.request.RequestDocumentation</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_概览总结">14. 1 概览总结</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_1_1_核心实现包">14.1. 1.1 核心实现包：</h3>
<div class="ulist">
<ul>
<li>
<p><code>org.springframework.beans</code></p>
</li>
<li>
<p><code>org.springframework.context</code></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_1_2_核心对象">14.2. 1.2 核心对象：</h3>
<div class="ulist">
<ul>
<li>
<p>BeanFactory</p>
</li>
<li>
<p>ApplicationContext</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_容器">15. 2 容器</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring 容器里面的对象，都称之为Bean，同一由容器创建、分配、管理。</p>
</div>
<div class="paragraph">
<p>可以通过创建`ClassPathXmlApplicationContext` 或
<code>FileSystemXmlApplicationContext</code>
来创建容器。提供一些xml配置信息。不常用，在
测试中看到使用过，创建容器，获取bean对象。</p>
</div>
<div class="sect2">
<h3 id="_2_1_元信息">15.1. 2.1 元信息</h3>
<div class="paragraph">
<p>元信息，用于创建bean对象。元信息可以通过三种方式定义：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>xml 最原始的方式，所有bean均可通过xml定义</p>
</li>
<li>
<p>注解：例如@Service @Component,这些注解的类，会被创建bean对象</p>
</li>
<li>
<p>java 配置。可以再java类中进行bean对象，配置。结合 @Configuration,
@Bean，等等对象。</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_2_2_创建容器">15.2. 2.2 创建容器</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");</code></pre>
</div>
</div>
<div class="paragraph">
<p>xml均为需要配置的对象文件。应用场景不广，不做深入研究。</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_3_使用容器">15.3. 2.3 使用容器</h3>
<div class="paragraph">
<p>容器，实质就是bean对象的一个集合，可以从容器中获取想要的bean对象，bean默认是单例。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">// 创建容器
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// 获取对象
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 使用对象方法
List&lt;String&gt; userList = service.getUsernameList();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_2_4_bean_对象">15.4. 2.4 Bean 对象</h3>
<div class="sect3">
<h4 id="_2_4_1_bean_命名">2.4.1 bean 命名</h4>
<div class="paragraph">
<p>bean
对象可以有多个名称，但是必须全局唯一。可以通过xml通过id或者name属性设置。注解也可设置name属性，如果不设置，默认类名小写开头，驼峰。accountManager,
accountService, userDao, loginController 等等。</p>
</div>
<div class="paragraph">
<p>可以设置别名，等效，但是一般没用到过。</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_2_5_初始化_bean_的多种方式">15.5. 2.5 初始化 bean 的多种方式</h3>
<div class="paragraph">
<p>看了一遍，没啥感觉。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_依赖">16. 3 依赖</h2>
<div class="sectionbody">
<div class="paragraph">
<p>构造器注入，以及set注入，结合xml中属性进行对象配置。不太实用了。现在都是注解。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_4_bean_的scopes">17. 4 bean 的scopes</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>singleton
在IOC容器中只存在一个实例，也就是通过singleton作用域创建的对象是单例的。</p>
</li>
<li>
<p>prototype 第次从IOC容器中获取对象时，都返回一个新对象。</p>
</li>
<li>
<p>request
每次HTTP请求都会创建一个新的对象，当请求结束时则会自动销毁这个对象。该作用域只针对Web环境中使用。</p>
</li>
<li>
<p>session 同一个HTTP Session共享一个对象，不同的HTTP
Session使用不同的对象，当这个Session结束时销毁这个对象。该作用域也是只针对Web环境中使用的。</p>
</li>
<li>
<p>globalSession
同一个全局Session共享一个对象，该作用域也是只针对Web环境中使用的。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>注：还可以自定义作用范围。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_5_自定义_bean_nature">18. 5 自定义 bean nature</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>bean 生命周期，各种回调。</p>
</li>
<li>
<p>ApplicationContextAware and BeanNameAware</p>
</li>
<li>
<p>其他 Aware 接口</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_5_1_生命周期回调">18.1. 5.1 生命周期回调</h3>
<div class="paragraph">
<p>方式： - 实现 InitializingBean 、DisposableBean
，实现方法，从而实现bean初始化、销毁时回调函数。 - 使用JSRO-250
@PostConstruct @PreDestroy 注解，在bean对象创建前，销毁后执行。</p>
</div>
<div class="sect3">
<h4 id="_5_1_1_初始化回调">5.1.1 初始化回调</h4>
<div class="ulist">
<ul>
<li>
<p>实现 InitializingBean ，并实现 afterPropertiesSet 方法，即可</p>
</li>
<li>
<p>使用注解 @PostConstruct 标注初始化方法。</p>
</li>
<li>
<p>使用 init-method 属性（xml中）， initMethod 属性（@Bean）</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_5_1_2_销毁时回调">5.1.2 销毁时回调</h4>
<div class="ulist">
<ul>
<li>
<p>实现 DisposableBean 接口</p>
</li>
<li>
<p>使用 @PreDestroy 标注方法</p>
</li>
<li>
<p>使用 destroy-method 属性（xml中）， destroyMethod 属性（@Bean）</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_5_2_启动_和_关闭_回调">18.2. 5.2 启动 和 关闭 回调</h3>
<div class="paragraph">
<p>有些特殊场景，一些对象有自己独特的生命周期需要，可以设置启动或者关闭时回调。核心接口
<code>Lifecycle</code>。</p>
</div>
</div>
<div class="sect2">
<h3 id="_5_3_非web应用_关闭_ioc_容器时回调">18.3. 5.3 非web应用，关闭 IOC 容器时回调</h3>

</div>
<div class="sect2">
<h3 id="_5_4_aware">18.4. 5.4 Aware</h3>
<div class="paragraph">
<p>感知、意识到。使用Aware，将与Spring耦合。
<a href="https://my.oschina.net/u/4149877/blog/3076044" class="bare">https://my.oschina.net/u/4149877/blog/3076044</a></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_6_bean_定义继承">19. 6 Bean 定义继承</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Bean，如果继承父类，父类bean的属性可以被子类bean继承。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_7_容器扩展方法">20. 7 容器扩展方法</h2>
<div class="sectionbody">
<div class="paragraph">
<p>忽略</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_8_基于注解的容器配置">21. 8 基于注解的容器配置</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_8_1_required">21.1. 8.1 @Required</h3>
<div class="paragraph">
<p>Spring 5.1
开始弃用此注解。此注解作用为，标注在配置期间，bean的属性必须存在。严格意义上检查，避免空指针。</p>
</div>
</div>
<div class="sect2">
<h3 id="_8_2_autowired">21.2. 8.2 @Autowired</h3>
<div class="paragraph">
<p>JSR 330 @Inject 作用相同。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>作用于构造器上，注入对象</p>
</li>
<li>
<p>作用于 setter 上，注入对象</p>
</li>
<li>
<p>作用于 方法上，注入对象</p>
</li>
<li>
<p>作用于 对象上</p>
</li>
<li>
<p>可混合使用</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>此外还有一个注解 @Resource, 此注解也能注入对象，但是有局限性，只支持
filed 以及setter方法，且只能有一个参数。</p>
</div>
<div class="paragraph">
<p>非必须：注入对象时，不一定需要此对象存在。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>@Autowired(required = false)</p>
</li>
<li>
<p>Optional</p>
</li>
<li>
<p>@Nullable</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>通过 @Order 或者 @Priority
注解，可以设置优先级。不使用注解，可以通过实现接口
org.springframework.core.Ordered</p>
</div>
</div>
<div class="sect2">
<h3 id="_8_3_primary">21.3. 8.3 @Primary</h3>
<div class="paragraph">
<p>注入对象的时候，默认是根据类型注入的，这时候可以指定优先级，则在注入的时候，优先注入级别高的。</p>
</div>
</div>
<div class="sect2">
<h3 id="_8_4_qualifier">21.4. 8.4 @Qualifier</h3>
<div class="paragraph">
<p>使用此注解, 可以指定注入命名的bean。在生成需要依赖的Bean对象时。</p>
</div>
<div class="paragraph">
<p>自定义 qualifier 注解。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
    String value();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>然后就可以用 @Genre 代替 @Qualifier。</p>
</div>
</div>
<div class="sect2">
<h3 id="_8_5_value">21.5. 8.5 @Value</h3>
<div class="ulist">
<ul>
<li>
<p>一般用于注入属性值</p>
</li>
<li>
<p>使用 EL 表达式，生成所需属性</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_9_classpath_扫描以及管理_components">22. 9 classpath 扫描以及管理 components</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_9_1_常见注解">22.1. 9.1 常见注解</h3>
<div class="paragraph">
<p>@Repository, @Component, @Service, and @Controller</p>
</div>
<div class="paragraph">
<p>根据业务功能选择合适的注解进行标注，如如controller层选用@Controller。尤其需要注意的是@Component，这个可以用来标识Bean，但非service层的。</p>
</div>
</div>
<div class="sect2">
<h3 id="_9_2_元注解和组合注解">22.2. 9.2 元注解和组合注解</h3>
<div class="paragraph">
<p>spring 提供的注解大多是元注解，可以组合其他注解，将功能进行结合。</p>
</div>
<div class="paragraph">
<p>例如 @RestController 包含@Controller and
@ResponseBody，可以实现2个注解的作用。</p>
</div>
<div class="paragraph">
<p>@Scope(WebApplicationContext.SCOPE_SESSION)</p>
</div>
<div class="paragraph">
<p>@SessionScope</p>
</div>
</div>
<div class="sect2">
<h3 id="_9_3_扫描">22.3. 9.3 扫描</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
   // ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>将 <code>org.example</code> 包下使用注解标注的Bean对象，创建、管理。</p>
</div>
</div>
<div class="sect2">
<h3 id="_9_4_过滤某些类">22.4. 9.4 过滤某些类。</h3>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-classpath-scanning" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-classpath-scanning</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_9_5_使用compoent_定义_bean_的元信息">22.5. 9.5 使用compoent 定义 Bean 的元信息</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Component
public class FactoryMethodComponent {

    @Bean @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>@Component 标注类。多个注解标注生成的Bean对象。</p>
</div>
</div>
<div class="sect2">
<h3 id="_9_6_bean_命名">22.6. 9.6 Bean 命名</h3>
<div class="paragraph">
<p>在定义Bean时，可以手动指定名称，也可以不指定，使用默认的bean name
生成策略（类名首字母小写，驼峰）。当然也可以自定义。</p>
</div>
<div class="paragraph">
<p>实现 BeanNameGenerator 接口。生成Bean对象时，指明name生成策略</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    // ...
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_9_7_使用_scope_指定_自动检测_components">22.7. 9.7 使用 scope 指定 自动检测 components</h3>
<div class="paragraph">
<p>使用 @Scope，默认是 singleton,
可以通过注解指定范围。也可以自定义范围解析类。</p>
</div>
<div class="paragraph">
<p>实现 <code>ScopeMetadataResolver</code> 接口</p>
</div>
<div class="paragraph">
<p>指定范围解析。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    // ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>还可以限制范围代理类型</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    // ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>scopedProxy: no, interfaces, and targetClass</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_10_使用_jsr_330_标准注解">23. 10 使用 JSR 330 标准注解。</h2>
<div class="sectionbody">
<div class="paragraph">
<p>需要加入jar</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">&lt;dependency&gt;
    &lt;groupId&gt;javax.inject&lt;/groupId&gt;
    &lt;artifactId&gt;javax.inject&lt;/artifactId&gt;
    &lt;version&gt;1&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>其实就是spring 相关注解的另一种同等作用注解。</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 34%;">
<col style="width: 33%;">
<col style="width: 33%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Spring</th>
<th class="tableblock halign-left valign-top">javax.inject.*</th>
<th class="tableblock halign-left valign-top">javax.inject restrictions / comments</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Autowired</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Inject</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Inject has no &#8216;required&#8217; attribute. Can be used
with Java 8’s Optional instead.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Component</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Named / @ManagedBean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">JSR-330 does not provide a
composable model, only a way to identify named components.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Scope(&#8220;singleton&#8221;)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Singleton</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The JSR-330 default scope is like
Spring’s prototype. However, in order to keep it consistent with
Spring’s general defaults, a JSR-330 bean declared in the Spring
container is a singleton by default. In order to use a scope other than
singleton, you should use Spring’s @Scope annotation. javax.inject also
provides a @Scope annotation. Nevertheless, this one is only intended to
be used for creating your own annotations.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Qualifier</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Qualifier / @Named</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">javax.inject.Qualifier is just a
meta-annotation for building custom qualifiers. Concrete String
qualifiers (like Spring’s @Qualifier with a value) can be associated
through javax.inject.Named.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Value</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no equivalent</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Required</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no equivalent</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@Lazy</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">no equivalent</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ObjectFactory</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provider</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">javax.inject.Provider is a direct alternative
to Spring’s ObjectFactory, only with a shorter get() method name. It can
also be used in combination with Spring’s @Autowired or with
non-annotated constructors and setter methods.</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>了解即可。还是使用spring。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_11_基于java代码_进行容器配置">24. 11 基于java代码，进行容器配置</h2>
<div class="sectionbody">
<div class="paragraph">
<p>主要是关于 @Comfiguration 以及 @Bean</p>
</div>
<div class="ulist">
<ul>
<li>
<p>@Configuration 重量级选手，在其配置内，可以解决依赖问题。</p>
</li>
<li>
<p>@Bean
轻量级，结合component注解，也能生成bean对象，但是如果内部复杂，有其他依赖，则无法实现。只使用于简单的bean对象生成。</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_11_1_import">24.1. 11.1 @Import</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>通过import，生成ConfigB中的Bean对象，也会同时生成ConfigA中的Bean、</p>
</div>
</div>
<div class="sect2">
<h3 id="_11_2_环境设置">24.2. 11.2 环境设置</h3>
<div class="paragraph">
<p>@Profile注解。 - @Profile(&#8220;development&#8221;) - @Profile(&#8220;production&#8221;)</p>
</div>
<div class="paragraph">
<p>development/production等均为自定义的环境变量值。可用于设置了某种环境下，执行对应的配置。</p>
</div>
<div class="paragraph">
<p>@PropertySource(&#8220;classpath:/com/myco/app.properties&#8221;)</p>
</div>
<div class="paragraph">
<p>指定属性源文件。</p>
</div>
</div>
<div class="sect2">
<h3 id="_11_3_spring_中的事件机制">24.3. 11.3 spring 中的事件机制</h3>
<div class="paragraph">
<p>步骤： - 定义事件 - 发送事件 - 监听事件，也即处理事件。</p>
</div>
<div class="sect3">
<h4 id="_11_3_1_定义事件">11.3.1 定义事件</h4>
<div class="paragraph">
<p>spring 内置了几类事件。</p>
</div>
<div class="paragraph">
<p><strong>自定义事件</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public class BlockedListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlockedListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_11_3_2_发送事件">11.3.2 发送事件</h4>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public class EmailService implements ApplicationEventPublisherAware {

    private List&lt;String&gt; blockedList;
    private ApplicationEventPublisher publisher;

    public void setBlockedList(List&lt;String&gt; blockedList) {
        this.blockedList = blockedList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blockedList.contains(address)) {
            publisher.publishEvent(new BlockedListEvent(this, address, content));
            return;
        }
        // send email...
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>ApplicationEventPublisher#publishEvent()，则可发送事件。</p>
</div>
</div>
<div class="sect3">
<h4 id="_11_3_3_监听事件_处理事件">11.3.3 监听事件，处理事件</h4>
<div class="paragraph">
<p>方式一：实现接口</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public class BlockedListNotifier implements ApplicationListener&lt;BlockedListEvent&gt; {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>方式二：使用注解@EventListener</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public class BlockedListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlockedListEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>@EventListener，还可以有些属性，进行针对性监听。可结合@Async、@Order(42)使用</p>
</div>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions</a></p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_基本java表达式计算">25. 1 基本java表达式计算</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_1_1_核心类">25.1. 1.1 核心类</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">ExpressionParser parser = new SpelExpressionParser();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_1_2_使用方式">25.2. 1.2 使用方式</h3>
<div class="paragraph">
<p><strong>字符串里面的表达式，只要符合java代码执行</strong>，则可以通过spring
el进行计算，并获取结果。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">String expression = "'Hello World'";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(expression);
String message = (String) exp.getValue();
message = exp.getValue(String.class);</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">String expression = "'Hello World'.concat('!')";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(expression);
String message = exp.getValue(String.class);</code></pre>
</div>
</div>
<div class="paragraph">
<p>获取结果，进行类型转换，可以指定结果类型，也可以强制类型转换，如果类型和实际不一样，会抛出异常
<code>EvaluationException</code>。</p>
</div>
<div class="paragraph">
<p>具体参考 <code>Expression#getValue()</code></p>
</div>
<div class="paragraph">
<p><strong>扩展：</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);

// Inventor 是一个对象
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");

ExpressionParser parser = new SpelExpressionParser();

// 解析 name ，name是 Inventor 一个属性
Expression exp = parser.parseExpression("name");

// 在 expression 中传入 Inventor 对象，则可以调用 name 属性
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"

exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_evaluationcontext">26. 2 EvaluationContext</h2>
<div class="sectionbody">
<div class="paragraph">
<p><code>EvaluationContext</code> 是一个接口，有2个实现，功能各有不同：</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>SimpleEvaluationContext</code> 只有部分spring el 特性。</p>
</li>
<li>
<p><code>StandardEvaluationContext</code> 更全的功能。</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_2_1_类型转换">26.1. 2.1 类型转换</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">class Simple {
    public List&lt;Boolean&gt; booleanList = new ArrayList&lt;Boolean&gt;();
}

Simple simple = new Simple();
simple.booleanList.add(true);

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// 这里设置 `booleanList[0]` 为 `false`, 传入的是一个 `false` 字符串，但是能自动转为 `boolean` 类型，并设置到 `booleanList[0]` 中。
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");

// b is false
Boolean b = simple.booleanList.get(0);</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_解析配置">27. 3 解析配置</h2>
<div class="sectionbody">
<div class="paragraph">
<p>核心类 <code>SpelParserConfiguration</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">class Demo {
    public List&lt;String&gt; list;
}

// 第一个参数，开启null属性，自动赋值，第二个参数，设置集合动态扩容。
SpelParserConfiguration config = new SpelParserConfiguration(true,true);

ExpressionParser parser = new SpelExpressionParser(config);

Expression expression = parser.parseExpression("list[3]");

Demo demo = new Demo();

// 表达式中是想获取 list[3] 的值，由于配置了默认初始化以及扩容，这里就会使 list 自动扩容 length 为 4，每个值为空字符串。
Object o = expression.getValue(demo);</code></pre>
</div>
</div>
<div class="paragraph">
<p>通过配置 <code>SpelParserConfiguration</code> 配置2个参数。
空值初始化，以及集合自动扩展，值为指定类型的默认值。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_4_编译_compiler_vs_解释_interpreter">28. 4 编译 compiler vs 解释 interpreter</h2>
<div class="sectionbody">
<div class="paragraph">
<p>spring
框架最初提供的就是解释模式，但是性能不佳，后面提供编译模式选择，会在计算的时候生成class，提高计算性能。默认是不开启编译模式的。</p>
</div>
<div class="sect2">
<h3 id="_4_1_编译模式有3中选项">28.1. 4.1 编译模式有3中选项：</h3>
<div class="paragraph">
<p><code>org.springframework.expression.spel.SpelCompilerMode</code> 类中 - OFF
(default): 默认关闭. -
IMMEDIATE：尽可能快地进行表达式编译，如果失败，抛出异常 - MIXED：
混合，编译和解释2种状态转换，首选编译，如果get wrong , 则切换至解释。</p>
</div>
</div>
<div class="sect2">
<h3 id="_4_2_示例">28.2. 4.2 示例</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
    this.getClass().getClassLoader());

SpelExpressionParser parser = new SpelExpressionParser(config);

Expression expr = parser.parseExpression("payload");

MyMessage message = new MyMessage();

Object payload = expr.getValue(message);</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_4_3_第二种设置_compiler_方式">28.3. 4.3 第二种设置 compiler 方式</h3>
<div class="paragraph">
<p>设置 jvm 属性。
<a href="https://docs.spring.io/spring-framework/docs/current/reference/html/appendix.html#appendix-spring-properties" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/appendix.html#appendix-spring-properties</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_4_4_complier_限制">28.4. 4.4 complier 限制</h3>
<div class="ulist">
<ul>
<li>
<p>表达式中涉及任务</p>
</li>
<li>
<p>Expressions relying on the conversion service</p>
</li>
<li>
<p>Expressions using custom resolvers or accessors</p>
</li>
<li>
<p>Expressions using selection or projection</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_5_在定义中使用表达式">29. 5 在定义中使用表达式</h2>
<div class="sectionbody">
<div class="paragraph">
<p>xml、以及注解方式，xml就不用了。注解使用 <code>#{ &lt;expression string&gt; }</code>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>在属性上使用</p>
</li>
<li>
<p>在set方法上使用</p>
</li>
<li>
<p>在方法参数前使用</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;

@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
    this.defaultLocale = defaultLocale;
}

@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
    this.movieFinder = movieFinder;
        this.defaultLocale = defaultLocale;
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_6_常见的表达式使用">30. 6 常见的表达式使用</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_6_1_字面量表达式">30.1. 6.1 字面量表达式</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_6_2_属性_数组_集合表达式">30.2. 6.2 属性、数组、集合表达式</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">// evals to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);

String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// Inventions Array

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
        context, tesla, String.class);

// Members List

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(
        context, ieee, String.class);

// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
        context, ieee, String.class);</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_6_3_其他总结下_具体看官网">30.3. 6.3 其他总结下，具体看官网</h3>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">符号</th>
<th class="tableblock halign-left valign-top">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">lt (&lt;)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">gt (&gt;)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">le (&#8656;)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ge (&gt;=)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">eq (==)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ne (!=)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">div (/)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">mod (%)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">not (!)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">and (&amp;&amp;)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">or (||)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">T(java.util.Date)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">函数，java.lang
下函数只需要类名，其他的需要包名加类名</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">#variableName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">获取变量</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">#this</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">当前</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@something</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bean对象</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">alse ? &#8216;trueExp&#8217; : &#8216;falseExp&#8217;</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If-Then-Else 三目运算符</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name?:'`Unknown&#8217;</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">空值赋值默认值</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">placeOfBirth?.city</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">防止空指针异常</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">.?[selectionExpression]</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">集合筛选</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">.![projectionExpression]</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">集合推断</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">#\{ }</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">表达式模板</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>具体用法：https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-properties-arrays</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache</a></p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p><strong>基础环境：spring 5</strong></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_maven坐标">31. 1. maven坐标</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-cache&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_启用_enablecaching">32. 2. 启用 @EnableCaching</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>需要在spring容器中使用。</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Configuration
@EnableCaching
public class AppConfig {
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_注解功能">33. 3. 注解功能</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>@Cacheable: 缓存某个数据</p>
</li>
<li>
<p>@CacheEvict: 清除某个缓存，或者所有缓存</p>
</li>
<li>
<p>@CachePut:
缓存某个数据，但是会调用函数，一般用于新增或者修改某个数据，然后更新缓存值。</p>
</li>
<li>
<p>@Caching: 配置于函数上，组合多个Cache注解使用。</p>
</li>
<li>
<p>@CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_4_cacheable_缓存数据">34. 4. @Cacheable 缓存数据。</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>作用：</strong>当触发函数调用时，会先从缓存中查询数据，如果没有，则执行函数，并将结果缓存起来。其中注解的一些属性，需要仔细了解下。</p>
</div>
<div class="sect2">
<h3 id="_4_1_源码">34.1. 4.1 源码</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {

    // value 和 cacheNames 相同，均表示存放命名空间，可多个。
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

    // 键，缓存的值，是以key-value的形式存放，如果不设置，会使用键的生成规则产生一个key
    String key() default "";

    // 键生成规则 和 key 属性互斥，如果同时使用会抛异常
    String keyGenerator() default "";

    // 缓存管理器 TODO 功能未研究
    String cacheManager() default "";
    // 缓存解析器 TODO 功能未研究
    String cacheResolver() default "";

    // 缓存条件，满足指定条件的才进行缓存，spring el 表达式
    String condition() default "";

    // 缓存条件，不同于condition，可在结果执行完后进行判断操作
    String unless() default "";

    // 是否同步，多线程环境，可能会多次计算缓存，依赖于缓存插件是否支持，比如encache等。
    boolean sync() default false;
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_4_2_键的默认生成策略">4.2 键的默认生成策略：</h4>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-cacheable-default-key" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-cacheable-default-key</a></p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_4_3_使用">34.2. 4.3 使用</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    @Cacheable(cacheNames = "cache", key = "#p0")
    public double getOne(String id) {
        return Math.random();
    }</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_5_cacheevict_清除缓存">35. 5 @CacheEvict 清除缓存</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic">
<li>
<p>可以清楚指定的一个缓存</p>
</li>
<li>
<p>也可以清楚某个命名空间下的所有缓存。</p>
</li>
</ol>
</div>
<div class="sect2">
<h3 id="_5_1_源码">35.1. 5.1 源码</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
    // 命名空间，用于标识清除哪个名字的缓存，同cacheNames
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};
    // 指定清除某一个缓存
    String key() default "";
    // 指定缓存key生成策略，根据策略生成的key，进行清除
    String keyGenerator() default "";
    // 缓存管理器 TODO 功能待研究
    String cacheManager() default "";
    // 缓存解析器 TODO 功能待研究
    String cacheResolver() default "";
    // 清除条件
    String condition() default "";
    // 是否清除指定value下的所有缓存
    boolean allEntries() default false;

    boolean beforeInvocation() default false;
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_6_cacheput">36. 6 @CachePut</h2>
<div class="sectionbody">
<div class="paragraph">
<p>也用于缓存某个数据，不同于@Cacheable,
@CachePut，无论如何都会调用函数执行，然后进行缓存操作，常用于新增或者修改，需要更新缓存。</p>
</div>
<div class="sect2">
<h3 id="_6_2_源码">36.1. 6.2 源码</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">**package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
    // 命名空间，用于将缓存存放在哪个缓存名下。同cacheNames
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};
    // 指定键
    String key() default "";
    // 指定键生成规则，与key互斥，同时存在，抛异常
    String keyGenerator() default "";
    // 缓存管理器，TODO 未研究
    String cacheManager() default "";
    // 缓存解析器，TODO 未研究
    String cacheResolver() default "";
    // 条件。入参
    String condition() default "";
    // 条件。返回值。
    String unless() default "";
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_7_caching">37. 7 @Caching</h2>
<div class="sectionbody">
<div class="paragraph">
<p>从源码中看，这是一个综合性注解。包含 Cacheable 、CachePut、 CacheEvict
三种功能，都可以分别进行设置。</p>
</div>
<div class="sect2">
<h3 id="_7_2_源码">37.1. 7.2 源码</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_8_cacheconfig">38. 8 @CacheConfig</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_8_1_源码">38.1. 8.1 源码</h3>
<div class="paragraph">
<p>提取了 Cacheable 、CachePut、 CacheEvict、Caching
四种注解共有属性，作用于类上，这样，类中使用这些注解的都采用CacheConfig
设置的属性，可以不用每个都写相同的属性。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
    // 缓存名称
    String[] cacheNames() default {};
    // 键生成策略
    String keyGenerator() default "";
    // 缓存管理器
    String cacheManager() default "";
    // 缓存解析器
    String cacheResolver() default "";
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_需求">39. 需求</h2>
<div class="sectionbody">
<div class="paragraph">
<p>springboot项目启动配置过多，每次启动测试类需要加载整个环境配置，导致启动缓慢，不利于快速测试，部分场景只需要少量以来配置，因此可自定义加载配置。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_方法">40. 方法</h2>
<div class="sectionbody">
<div class="paragraph">
<p>这里以测试controller为例，由于只测试controller，因此可以不需要启动什么数据库、缓存之类的，只需要加载基础的bean对象即可。</p>
</div>
<div class="literalblock">
<div class="content">
<pre>package org.**.aop;

import org.**.controller.LogController;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.*;
import org.springframework.test.web.servlet.result.*;

@WebMvcTest(LogController.class)
@ContextConfiguration(classes = Configuration.class)
class LogAspectTest {

    @Autowired
    private MockMvc mvc;

    @Test
    void given_login_when_request_then_save_this_request() {
        Assertions.assertTrue(true);
    }
}</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>package org.**.aop;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

/**
 * @author wanghengzhi
 * @since 2021/11/8 15:18
 */
@SpringBootConfiguration
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
        basePackages = "org.**")
public class Configuration {

}</pre>
</div>
</div>
<div class="paragraph">
<p>方法梳理：</p>
</div>
<div class="paragraph">
<p>在测试类中，指定启动spring环境上下文配置，并指向自定义配置文件，这里配置文件类似于springboot默认启动类，可以在这里配置依赖配置。</p>
</div>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#spring-mvc-test-framework" class="bare">https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#spring-mvc-test-framework</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_创建mockmvc_对象">41. 1 创建MockMvc 对象</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">package com.wanghengzhi.dadada;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * 测试启动类
 * @author wanghengzhi
 * @since 2020.9.2
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationTest {

    protected MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;
    /**
     * jackson
     */
    @Autowired
    protected ObjectMapper jacksonMapper;

    /**
     * 构造MockMvc
     */
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void empty() {
        System.out.println("Begin Testing With TDD !");
    }

}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_发起请求">42. 2 发起请求</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_2_1_构建请求的工具类">42.1. 2.1 构建请求的工具类</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>MockMvcRequestBuilders 构造request.</p>
</li>
<li>
<p>MockMvcResultHandlers 构造结果处理类，怎样处理结果。例如打印response</p>
</li>
<li>
<p>MockMvcResultMatchers
构造结果预期类，相当于junit的断言。例如判断http状态是否是预期状态。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>这3个类对应整个http请求的3个核心步骤。请求、处理、返回。</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_2_一次完整的http请求以及处理流程">42.2. 2.2 一次完整的http请求以及处理流程</h3>
<div class="paragraph">
<p><strong>主要通过MockMvc对象。</strong></p>
</div>
<div class="paragraph">
<p>使用分步构造对象，不使用链式调用，阅读不直观，且常包含中文，虽然不建议肉眼打印中文进行测试判断，但是有时候还是需要。</p>
</div>
<div class="paragraph">
<p><strong>反例：</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">mockMvc.perform(post(url)
        .contentType(MediaType.APPLICATION_JSON_VALUE)
        .header(TestConstant.HEADER_TOKEN, TestConstant.USER_ADMIN_TOKEN)
        .content(jacksonMapper.writeValueAsString(vo))
)
        .andExpect(status().isOk())
        .andDo(print());</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>推荐</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">String url = BASE_URL + "/abc";

// 第一步，构建request
MockHttpServletRequestBuilder request = post(url).contentType(MediaType.APPLICATION_JSON_VALUE)
        .header(TestConstant.HEADER_TOKEN, TestConstant.USER_ADMIN_TOKEN)
        .content(jacksonMapper.writeValueAsString(new ArraList()));

// 执行请求
ResultActions perform = super.mockMvc.perform(request);

// 设置返回数据编码
// 设置response编码，防止print时中文乱码。默认是iso-8859-1。
perform.andReturn().getResponse().setCharacterEncoding("utf-8");

// 对响应的数据进行判断
perform.andExpect(status().isOk())
        .andDo(print());</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_2_3_mockmvcrequestbuilders">42.3. 2.3 MockMvcRequestBuilders</h3>
<div class="paragraph">
<p>参考代码</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_4_mockmvcresultmatchers">42.4. 2.4 MockMvcResultMatchers</h3>
<div class="paragraph">
<p>参考代码</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_5_mockmvcresulthandlers">42.5. 2.5 MockMvcResultHandlers</h3>
<div class="paragraph">
<p>参考代码</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_总结">43. 3 总结</h2>
<div class="sectionbody">
<div class="paragraph">
<p>实质上就是一次http请求，等同于postman、浏览器等测试工具，只不过更优的是进行代码化，利于测试。属于集成测试范畴。</p>
</div>
<div class="paragraph">
<p><a href="https://www.jianshu.com/p/994027425b44" class="bare">https://www.jianshu.com/p/994027425b44</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_静态包">44. 静态包</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.powermock.api.mockito.PowerMockito.*;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_类">45. 类</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">@RunWith(PowerMockRunner.class)
@PrepareForTest({YouStaticClass.class})</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mock以及初始化">46. mock以及初始化</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    /**
     * 使用 @InjectMocks, 测试对象
     */
    @InjectMocks
    private TestService ts;

    /**
     * 使用@mock，测试对象依赖的对象
     */
    @Mock
    private DependencyService ds;

    @Before
    public void before(){
    // 使模拟生效
        PowerMockito.mockStatic(YouStaticClass.class);
        MockitoAnnotations.initMocks(this);
    }</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_环境">47. 环境</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>springboot 2.2.6</p>
</li>
<li>
<p>junit5</p>
</li>
<li>
<p>@WebMvcTest</p>
</li>
<li>
<p>MockMvc</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_乱码问题">48. 乱码问题</h2>
<div class="sectionbody">
<div class="paragraph">
<p>使用 MockMvc 执行 http 请求，得到的响应后，print(), 控制台中文乱码。</p>
</div>
<div class="literalblock">
<div class="content">
<pre>{"code":10001,"message":"è¯·æ±æ¹å¼ä¸æ¯æ","data":"Request method 'POST' not supported","responseTime":"2022-05-27T14:19:45.806"}</pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_原因">49. 原因</h2>
<div class="sectionbody">
<div class="paragraph">
<p>响应体中：</p>
</div>
<div class="literalblock">
<div class="content">
<pre>Headers = [Content-Type:"application/json;charset=ISO-8859-1"]</pre>
</div>
</div>
<div class="paragraph">
<p>源码位置 MockHttpServletResponse#characterEncoding</p>
</div>
<div class="literalblock">
<div class="content">
<pre>// ISO-8859-1
@Nullable
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;</pre>
</div>
</div>
<div class="paragraph">
<p>我也不知道为什么要设置成默认这个编码。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_解决办法">50. 解决办法</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_方式一_将请求后得到的response_修改其编码值">50.1. 方式一：将请求后得到的response，修改其编码值</h3>
<div class="literalblock">
<div class="content">
<pre>mockMvc.perform(post("/xxx")).andReturn().getResponse().setCharacterEncoding(WebUtils.DEFAULT_CHARACTER_ENCODING);</pre>
</div>
</div>
<div class="paragraph">
<p>缺点：每次都要调用处理，麻烦。</p>
</div>
</div>
<div class="sect2">
<h3 id="_方式二_全局拦截_推荐">50.2. 方式二：全局拦截 (推荐)</h3>
<div class="paragraph">
<p>创建mockMvc，增加filter。</p>
</div>
<div class="literalblock">
<div class="content">
<pre>    private MockMvc mockMvc;
    @BeforeEach
    void before(WebApplicationContext context) {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .addFilter((request, response, chain) -&gt; {
                    response.setCharacterEncoding("UTF-8");
                    chain.doFilter(request, response);
                }, "/*")
                .build();
    }</pre>
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>原文： <a href="https://github.com/hehonghui/mockito-doc-zh" class="bare">https://github.com/hehonghui/mockito-doc-zh</a></p>
</div>
</blockquote>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mockito_中文文档_2_0_26_beta">51. Mockito 中文文档 ( 2.0.26 beta )</h2>
<div class="sectionbody">
<div class="paragraph">
<p><span class="image"><img src="http://img.blog.csdn.net/20150731162529393" alt="http://img.blog.csdn.net/20150731162529393"></span></p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>由于缺乏校对，难免有谬误之处，如果发现任何语句不通顺、翻译错误，都可以在https://github.com/hehonghui/mockito-doc-zh[github中的项目]提出issue。谢谢~</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Mockito框架官方地址http://mockito.org/[mockito]，http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[文档地址]。</p>
</div>
<div class="paragraph">
<p>Mockito库能够Mock对象、验证结果以及打桩(stubbing)。</p>
</div>
<div class="paragraph">
<p>该文档您也可以通过http://mockito.org获取到。所有文档都保存在javadocs中，因为它能够保证文档与源代码的一致性。这样也能够让离线的用户从IDE直接访问到文档。这样一来也能够激励Mockito开发者在每次写代码、每次提交时更新对应的文档。</p>
</div>
<div class="sect2">
<h3 id="_参与人员">51.1. 参与人员</h3>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">成员</th>
<th class="tableblock halign-left valign-top">任务</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/hehonghui/">Mr.Simple</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1-15</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/chaosss/">chaosss</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">16-26</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/tiiime/">tiiime</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">27~35</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/dengshiwei/">dengshiwei</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">a~c开头的方法,包含</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/objectlife/">objectlife</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">d~m开头的方法</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/Conquer/">Conquer</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">n-w开头的函数</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_目录">51.2. 目录</h3>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p><a href="#0">迁移到Mockito 2.0</a></p>
</li>
<li>
<p><a href="#1">验证某些行为</a></p>
</li>
<li>
<p><a href="#2">如何做一些测试桩 (Stub)</a></p>
</li>
<li>
<p><a href="#3">参数匹配器 (matchers)</a></p>
</li>
<li>
<p><a href="#4">验证函数的确切、最少、从未调用次数</a></p>
</li>
<li>
<p><a href="#5">为返回值为void的函数通过Stub抛出异常</a></p>
</li>
<li>
<p><a href="#6">按照顺序验证执行结果</a></p>
</li>
<li>
<p><a href="#7">确保交互(interaction)操作不会执行在mock对象上</a></p>
</li>
<li>
<p><a href="#8">查找冗余的调用</a></p>
</li>
<li>
<p><a href="#9">简化mock对象的创建</a></p>
</li>
<li>
<p><a href="#10">为连续的调用做测试桩 (stub)</a></p>
</li>
<li>
<p><a href="#11">为回调做测试桩</a></p>
</li>
<li>
<p><a href="#12">doReturn()、doThrow()、doAnswer()、doNothing()、doCallRealMethod()系列方法的运用</a></p>
</li>
<li>
<p><a href="#13">监控真实对象</a></p>
</li>
<li>
<p><a href="#14">修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )</a></p>
</li>
<li>
<p><a href="#15">为下一步的断言捕获参数 (1.8版本之后)</a></p>
</li>
<li>
<p><a href="#16">真实的局部mocks (1.8版本之后)</a></p>
</li>
<li>
<p><a href="#17">重置mocks对象 (1.8版本之后)</a></p>
</li>
<li>
<p><a href="#18">故障排查与验证框架的使用 (1.8版本之后)</a></p>
</li>
<li>
<p><a href="#19">行为驱动开发的别名 (1.8版本之后)</a></p>
</li>
<li>
<p><a href="#20">序列化mock对象</a></p>
</li>
<li>
<p><a href="#21">新的注解 : @Captor&#44;@Spy&#44;@ InjectMocks
(1.8.3版本之后)</a></p>
</li>
<li>
<p><a href="#22">验证超时 (1.8.5版本之后)</a></p>
</li>
<li>
<p><a href="#23">自动初始化被@Spies&#44;
@InjectMocks注解的字段以及构造函数注入 (1.9.0版本之后)</a></p>
</li>
<li>
<p><a href="#24">单行测试桩 (1.9.0版本之后)</a></p>
</li>
<li>
<p><a href="#25">验证被忽略的测试桩 (1.9.0版本之后)</a></p>
</li>
<li>
<p><a href="#26">mock详情 (1.9.5版本之后)</a></p>
</li>
<li>
<p><a href="#27">delegate调用真实的实例 (1.9.5版本之后)</a></p>
</li>
<li>
<p><a href="#28">MockMaker API (1.9.5版本之后)</a></p>
</li>
<li>
<p><a href="#29">BDD风格的验证 (1.10.0版本之后)</a></p>
</li>
<li>
<p><a href="#30">追踪或者Mock抽象类 (1.10.12版本之后)</a></p>
</li>
<li>
<p><a href="#31">Mockito mock对象通过ClassLoader能被序列化/反序列化
(1.10.0版本之后)</a></p>
</li>
<li>
<p><a href="#32">deep stubs更好的支持泛型 (1.10.0版本之后)</a></p>
</li>
<li>
<p><a href="#33">Mockito JUnit 规则 (1.10.17版本之后)</a></p>
</li>
<li>
<p><a href="#34">开/关插件 (1.10.15版本之后)</a></p>
</li>
<li>
<p><a href="#35">自定义验证失败消息 (2.0.0版本之后)</a></p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_0_迁移到mockito_2_0">51.3. 0. 迁移到Mockito 2.0</h3>
<div class="paragraph">
<p>为了持续提升Mockito以及更进一步的提升单元测试体验,我们希望你升级到Mockito
2.0.Mockito遵循语意化的版本控制，除非有非常大的改变才会变化主版本号。在一个库的生命周期中,为了引入一系列有用的特性，修改已存在的行为或者API等重大变更是在所难免的。因此，我们希望你能够爱上
Mockito 2.0!</p>
</div>
<div class="paragraph">
<p>重要变更 :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Mockito从Hamcrest中解耦，自定义的matchers
API也发生了改变,查看http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentMatcher.html[ArgumentMatcher]
的基本原理以及迁移指南。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>跟着我们的示例来mock 一个List,因为大家都知道它的接口（例如add(),get(),
clear()）。不要mock一个真实的List类型,使用一个真实的实例来替代。</p>
</div>
</div>
<div class="sect2">
<h3 id="_1_验证某些行为">51.4. 1. 验证某些行为</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> // 静态导入会使代码更简洁
 import static org.mockito.Mockito.*;

 // mock creation 创建mock对象
 List mockedList = mock(List.class);

 //using mock object 使用mock对象
 mockedList.add("one");
 mockedList.clear();

 //verification 验证
 verify(mockedList).add("one");
 verify(mockedList).clear();</code></pre>
</div>
</div>
<div class="paragraph">
<p>一旦mock对象被创建了，mock对象会记住所有的交互。然后你就可能选择性的验证你感兴趣的交互。</p>
</div>
</div>
<div class="sect2">
<h3 id="_2_link_如何做一些测试桩_stub">51.5. 2. link:[如何做一些测试桩 (Stub)]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //You can mock concrete classes, not only interfaces
 // 你可以mock具体的类型,不仅只是接口
 LinkedList mockedList = mock(LinkedList.class);

 //stubbing
 // 测试桩
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());

 //following prints "first"
 // 输出“first”
 System.out.println(mockedList.get(0));

 //following throws runtime exception
 // 抛出异常
 System.out.println(mockedList.get(1));

 //following prints "null" because get(999) was not stubbed
 // 因为get(999) 没有打桩，因此输出null
 System.out.println(mockedList.get(999));

 //Although it is possible to verify a stubbed invocation, usually it's just redundant
 //If your code cares what get(0) returns then something else breaks (often before even verify() gets executed).
 //If your code doesn't care what get(0) returns then it should not be stubbed. Not convinced? See here.
 // 验证get(0)被调用的次数
 verify(mockedList).get(0);</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>默认情况下，所有的函数都有返回值。mock函数默认返回的是null，一个空的集合或者一个被对象类型包装的内置类型，例如0、false对应的对象类型为Integer、Boolean；</p>
</li>
<li>
<p>测试桩函数可以被覆写 :
例如常见的测试桩函数可以用于初始化夹具，但是测试函数能够覆写它。请注意，覆写测试桩函数是一种可能存在潜在问题的做法；</p>
</li>
<li>
<p>一旦测试桩函数被调用，该函数将会一致返回固定的值；</p>
</li>
<li>
<p>上一次调用测试桩函数有时候极为重要-当你调用一个函数很多次时，最后一次调用可能是你所感兴趣的。</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_3_link_参数匹配器_matchers">51.6. 3. link:[参数匹配器 (matchers)]</h3>
<div class="paragraph">
<p>Mockito以自然的java风格来验证参数值:
使用equals()函数。有时，当需要额外的灵活性时你可能需要使用参数匹配器，也就是argument
matchers :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //stubbing using built-in anyInt() argument matcher
 // 使用内置的anyInt()参数匹配器
 when(mockedList.get(anyInt())).thenReturn("element");

 //stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
 // 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 )
 when(mockedList.contains(argThat(isValid()))).thenReturn("element");

 //following prints "element"
 // 输出element
 System.out.println(mockedList.get(999));

 //you can also verify using an argument matcher
 // 你也可以验证参数匹配器
 verify(mockedList).get(anyInt());</code></pre>
</div>
</div>
<div class="paragraph">
<p>参数匹配器使验证和测试桩变得更灵活。http://site.mockito.org/mockito/docs/current/org/mockito/Matchers.html[点击这里]查看更多内置的匹配器以及自定义参数匹配器或者hamcrest
匹配器的示例。</p>
</div>
<div class="paragraph">
<p>如果仅仅是获取自定义参数匹配器的信息，查看http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentMatcher.html[ArgumentMatcher类文档]即可。</p>
</div>
<div class="paragraph">
<p>为了合理的使用复杂的参数匹配，使用equals()与anyX()
的匹配器会使得测试代码更简洁、简单。有时，会迫使你重构代码以使用equals()匹配或者实现equals()函数来帮助你进行测试。</p>
</div>
<div class="paragraph">
<p>同时建议你阅读<a href="#sec_15">第15章节</a>或者http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentCaptor.html[ArgumentCaptor类文档]。ArgumentCaptor是一个能够捕获参数值的特俗参数匹配器。</p>
</div>
<div class="paragraph">
<p>参数匹配器的注意点 :</p>
</div>
<div class="paragraph">
<p>如果你使用参数匹配器,所有参数都必须由匹配器提供。</p>
</div>
<div class="paragraph">
<p>示例 : ( 该示例展示了如何多次应用于测试桩函数的验证 )</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
// 上述代码是正确的,因为eq()也是一个参数匹配器

verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument
// 上述代码是错误的,因为所有参数必须由匹配器提供，而参数"third argument"并非由参数匹配器提供，因此的缘故会抛出异常</code></pre>
</div>
</div>
<div class="paragraph">
<p>像anyObject(),
eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中，并且返回一个假的值，通常为null。<code>这样的实现是由于被Java编译器强加的静态类型安全</code>。结果就是你不能在验证或者测试桩函数之外使用anyObject(),
eq()函数。</p>
</div>
</div>
<div class="sect2">
<h3 id="_4_link_验证函数的确切_最少_从未调用次数">51.7. 4. link:[验证函数的确切、最少、从未调用次数]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //using mock
 mockedList.add("once");

 mockedList.add("twice");
 mockedList.add("twice");

 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");

 //following two verifications work exactly the same - times(1) is used by default
 // 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");

 //exact number of invocations verification
 // 验证具体的执行次数
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");

 //verification using never(). never() is an alias to times(0)
 // 使用never()进行验证,never相当于times(0)
 verify(mockedList, never()).add("never happened");

 //verification using atLeast()/atMost()
 // 使用atLeast()/atMost()
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("five times");
 verify(mockedList, atMost(5)).add("three times");</code></pre>
</div>
</div>
<div class="paragraph">
<p>verify函数默认验证的是执行了times(1)，也就是某个测试函数是否执行了1次.因此，times(1)通常被省略了。</p>
</div>
</div>
<div class="sect2">
<h3 id="_5_link_为返回值为void的函数通过stub抛出异常">51.8. 5. link:[为返回值为void的函数通过Stub抛出异常]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
// 调用这句代码会抛出异常
mockedList.clear();</code></pre>
</div>
</div>
<div class="paragraph">
<p>关于doThrow|doAnswer 等函数族的信息请阅读第十二章节。</p>
</div>
<div class="paragraph">
<p>最初，http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#stubVoid(T)[stubVoid(Object)]
函数用于为无返回值的函数打桩。现在stubVoid()函数已经过时,doThrow(Throwable)成为了它的继承者。这是为了提升与
doAnswer(Answer) 函数族的可读性与一致性。</p>
</div>
</div>
<div class="sect2">
<h3 id="_6_link_验证执行执行顺序">51.9. 6. link:[验证执行执行顺序]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> // A. Single mock whose methods must be invoked in a particular order
 // A. 验证mock一个对象的函数执行顺序
 List singleMock = mock(List.class);

 //using a single mock
 singleMock.add("was added first");
 singleMock.add("was added second");

 //create an inOrder verifier for a single mock
 // 为该mock对象创建一个inOrder对象
 InOrder inOrder = inOrder(singleMock);

 //following will make sure that add is first called with "was added first, then with "was added second"
 // 确保add函数首先执行的是add("was added first"),然后才是add("was added second")
 inOrder.verify(singleMock).add("was added first");
 inOrder.verify(singleMock).add("was added second");

 // B. Multiple mocks that must be used in a particular order
 // B .验证多个mock对象的函数执行顺序
 List firstMock = mock(List.class);
 List secondMock = mock(List.class);

 //using mocks
 firstMock.add("was called first");
 secondMock.add("was called second");

 //create inOrder object passing any mocks that need to be verified in order
 // 为这两个Mock对象创建inOrder对象
 InOrder inOrder = inOrder(firstMock, secondMock);

 //following will make sure that firstMock was called before secondMock
 // 验证它们的执行顺序
 inOrder.verify(firstMock).add("was called first");
 inOrder.verify(secondMock).add("was called second");

 // Oh, and A + B can be mixed together at will</code></pre>
</div>
</div>
<div class="paragraph">
<p>验证执行顺序是非常灵活的-你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。
另外，你可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。</p>
</div>
</div>
<div class="sect2">
<h3 id="_7_link_确保交互_interaction_操作不会执行在mock对象上">51.10. 7. link:[确保交互(interaction)操作不会执行在mock对象上]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //using mocks - only mockOne is interacted
 // 使用Mock对象
 mockOne.add("one");

 //ordinary verification
 // 普通验证
 verify(mockOne).add("one");

 //verify that method was never called on a mock
 // 验证某个交互是否从未被执行
 verify(mockOne, never()).add("two");

 //verify that other mocks were not interacted
 // 验证mock对象没有交互过
 verifyZeroInteractions(mockTwo, mockThree);</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_8_link_查找冗余的调用">51.11. 8. link:[查找冗余的调用]</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">//using mocks
mockedList.add("one");
mockedList.add("two");

verify(mockedList).add("one");

//following verification will fail
// 下面的验证将会失败
verifyNoMoreInteractions(mockedList);</code></pre>
</div>
</div>
<div class="paragraph">
<p>一些用户可能会在频繁地使用`verifyNoMoreInteractions()<code>，甚至在每个测试函数中都用。但是</code>verifyNoMoreInteractions()<code>并不建议在每个测试函数中都使用。</code>verifyNoMoreInteractions()`在交互测试套件中只是一个便利的验证，它的作用是当你需要验证是否存在冗余调用时。滥用它将导致测试代码的可维护性降低。你可以阅读http://monkeyisland.pl/2008/07/12/should-i-worry-about-the-unexpected/[这篇文档]来了解更多相关信息。</p>
</div>
<div class="paragraph">
<p>`never()`是一种更为明显且易于理解的形式。</p>
</div>
</div>
<div class="sect2">
<h3 id="_9_link_简化mock对象的创建">51.12. 9. link:[简化mock对象的创建]</h3>
<div class="ulist">
<ul>
<li>
<p>最小化重复的创建代码</p>
</li>
<li>
<p>使测试类的代码可读性更高</p>
</li>
<li>
<p>使验证错误更易于阅读，因为字段名可用于标识mock对象</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public class ArticleManagerTest {

   @Mock private ArticleCalculator calculator;
   @Mock private ArticleDatabase database;
   @Mock private UserProvider userProvider;

   private ArticleManager manager;</code></pre>
</div>
</div>
<div class="paragraph">
<p>注意！下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test
runner中:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> MockitoAnnotations.initMocks(testClass);</code></pre>
</div>
</div>
<div class="paragraph">
<p>你可以使用内置的runner:
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/runners/MockitoJUnitRunner.html">MockitoJUnitRunner</a>
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/runners/MockitoJUnitRunner.html">runner</a>
或者一个rule :
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/junit/MockitoJUnit.html#rule()">MockitoRule</a>。
关于mock注解的更多信息可以阅读http://site.mockito.org/mockito/docs/current/org/mockito/MockitoAnnotations.html[MockitoAnnotations文档]。</p>
</div>
</div>
<div class="sect2">
<h3 id="_10_link_为连续的调用做测试桩_stub">51.13. 10. link:[为连续的调用做测试桩 (stub)]</h3>
<div class="paragraph">
<p>有时我们需要为同一个函数调用的不同的返回值或异常做测试桩。典型的运用就是使用mock迭代器。
原始版本的Mockito并没有这个特性，例如，可以使用Iterable或者简单的集合来替换迭代器。这些方法提供了更自然的方式，在一些场景中为连续的调用做测试桩会很有用。示例如下
：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> when(mock.someMethod("some arg"))
   .thenThrow(new RuntimeException())
   .thenReturn("foo");

 //First call: throws runtime exception:
 // 第一次调用 : 抛出运行时异常
 mock.someMethod("some arg");

 //Second call: prints "foo"
 // 第二次调用 : 输出"foo"
 System.out.println(mock.someMethod("some arg"));

 //Any consecutive call: prints "foo" as well (last stubbing wins).
 // 后续调用 : 也是输出"foo"
 System.out.println(mock.someMethod("some arg"));</code></pre>
</div>
</div>
<div class="paragraph">
<p>另外，连续调用的另一种更简短的版本 :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">// 第一次调用时返回"one",第二次返回"two",第三次返回"three"
 when(mock.someMethod("some arg"))
   .thenReturn("one", "two", "three");</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_11_link_为回调做测试桩">51.14. 11. link:[为回调做测试桩]</h3>
<div class="paragraph">
<p>Allows stubbing with generic Answer interface.
运行为泛型接口Answer打桩。</p>
</div>
<div class="paragraph">
<p>在最初的Mockito里也没有这个具有争议性的特性。我们建议使用thenReturn()
或thenThrow()来打桩。这两种方法足够用于测试或者测试驱动开发。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> when(mock.someMethod(anyString())).thenAnswer(new Answer() {
     Object answer(InvocationOnMock invocation) {
         Object[] args = invocation.getArguments();
         Object mock = invocation.getMock();
         return "called with arguments: " + args;
     }
 });

 //Following prints "called with arguments: foo"
 // 输出 : "called with arguments: foo"
 System.out.println(mock.someMethod("foo"));</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_12">51.15. 12.</h3>
<div class="paragraph">
<p>link:[doReturn()、doThrow()、doAnswer()、doNothing()、doCallRealMethod()系列方法的运用]</p>
</div>
<div class="paragraph">
<p>通过`when(Object)`为无返回值的函数打桩有不同的方法,因为编译器不喜欢void函数在括号内…</p>
</div>
<div class="paragraph">
<p>使用`doThrow(Throwable)`
替换`stubVoid(Object)<code>来为void函数打桩是为了与</code>doAnswer()`等函数族保持一致性。</p>
</div>
<div class="paragraph">
<p>当你想为void函数打桩时使用含有一个exception 参数的`doAnswer()` :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
// 下面的代码会抛出异常
mockedList.clear();</code></pre>
</div>
</div>
<div class="paragraph">
<p>当你调用`doThrow()<code>, `doAnswer()</code>, <code>doNothing()</code>, <code>doReturn()</code>
and <code>doCallRealMethod()</code> 这些函数时可以在适当的位置调用`when()`函数.
当你需要下面这些功能时这是必须的:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>测试void函数</p>
</li>
<li>
<p>在受监控的对象上测试函数</p>
</li>
<li>
<p>不知一次的测试为同一个函数，在测试过程中改变mock对象的行为。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>但是在调用`when()`函数时你可以选择是否调用这些上述这些函数。</p>
</div>
<div class="paragraph">
<p>阅读更多关于这些方法的信息:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doReturn(java.lang.Object)">doReturn(Object)</a></p>
</li>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doThrow(java.lang.Throwable)">doThrow(Throwable)</a></p>
</li>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doThrow(java.lang.Class)">doThrow(Class)</a></p>
</li>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doAnswer(org.mockito.stubbing.Answer)">doAnswer(Answer)</a></p>
</li>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doNothing()">doNothing()</a></p>
</li>
<li>
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#doCallRealMethod()">doCallRealMethod()</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_13_link_监控真实对象">51.16. 13. link:[监控真实对象]</h3>
<div class="paragraph">
<p>你可以为真实对象创建一个监控(spy)对象。当你使用这个spy对象时真实的对象也会也调用，除非它的函数被stub了。尽量少使用spy对象，使用时也需要小心形式，例如spy对象可以用来处理遗留代码。</p>
</div>
<div class="paragraph">
<p>监控一个真实的对象可以与“局部mock对象”概念结合起来。在1.8之前，mockito的监控功能并不是真正的局部mock对象。原因是我们认为局部mock对象的实现方式并不好，在某些时候我发现一些使用局部mock对象的合法用例。（第三方接口、临时重构遗留代码，完整的文章在http://monkeyisland.pl/2009/01/13/subclass-and-override-vs-partial-mocking-vs-refactoring/[这里]
）</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">List list = new LinkedList();
List spy = spy(list);

//optionally, you can stub out some methods:
// 你可以为某些函数打桩
when(spy.size()).thenReturn(100);

//using the spy calls *real* methods
// 通过spy对象调用真实对象的函数
spy.add("one");
spy.add("two");

//prints "one" - the first element of a list
// 输出第一个元素
System.out.println(spy.get(0));

//size() method was stubbed - 100 is printed
// 因为size()函数被打桩了,因此这里返回的是100
System.out.println(spy.size());

//optionally, you can verify
// 交互验证
verify(spy).add("one");
verify(spy).add("two");</code></pre>
</div>
</div>
<div class="paragraph">
<p>理解监控真实对象非常重要！</p>
</div>
<div class="paragraph">
<p>有时，在监控对象上使用`when(Object)<code>来进行打桩是不可能或者不切实际的。因此，当使用监控对象时请考虑</code>doReturn|Answer|Throw()`函数族来进行打桩。例如
:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">List list = new LinkedList();
List spy = spy(list);

//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
// 不可能 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常，因为真实List对象是空的
   when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing
// 你需要使用doReturn()来打桩
doReturn("foo").when(spy).get(0);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Mockito并不会为真实对象代理函数调用，实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互，不要期望从监控对象得到正确的结果。当你在监控对象上调用一个没有被stub的函数时并不会调用真实对象的对应函数，你不会在真实对象上看到任何效果。</p>
</div>
<div class="paragraph">
<p>因此结论就是 :
当你在监控一个真实对象时，你想在stub这个真实对象的函数，那么就是在自找麻烦。或者你根本不应该验证这些函数。</p>
</div>
</div>
<div class="sect2">
<h3 id="_14_link_修改没有测试桩的调用的默认返回值_1_7版本之后">51.17. 14. link:[修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )]</h3>
<div class="paragraph">
<p>你可以指定策略来创建mock对象的返回值。这是一个高级特性，通常来说，你不需要写这样的测试。然后，它对于遗留系统来说是很有用处的。当你不需要为函数调用打桩时你可以指定一个默认的answer。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnAnswer());</code></pre>
</div>
</div>
<div class="paragraph">
<p>关于RETURNS_SMART_NULLS更多的信息请查看 :
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#RETURNS_SMART_NULLS">RETURNS_SMART_NULLS文档</a>
。</p>
</div>
</div>
<div class="sect2">
<h3 id="_15_为下一步的断言捕获参数_1_8版本之后">51.18. 15. 为下一步的断言捕获参数 (1.8版本之后)</h3>
<div class="paragraph">
<p>Mockito以java代码风格的形式来验证参数值 :
即通过使用`equals()`函数。这也是我们推荐用于参数匹配的方式，因为这样会使得测试代码更简单、简洁。在某些情况下，当验证交互之后要检测真实的参数值时这将变得有用。例如
：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">ArgumentCaptor&lt;Person&gt; argument = ArgumentCaptor.forClass(Person.class);
// 参数捕获
verify(mock).doSomething(argument.capture());
// 使用equal断言
assertEquals("John", argument.getValue().getName());</code></pre>
</div>
</div>
<div class="paragraph">
<p>警告 :
我们建议使用没有测试桩的ArgumentCaptor来验证，因为使用含有测试桩的ArgumentCaptor会降低测试代码的可读性，因为captor是在断言代码块之外创建的。另一个好处是它可以降低本地化的缺点，因为如果测试桩函数没有被调用，那么参数就不会被捕获。总之，ArgumentCaptor与自定义的参数匹配器相关(可以查看<a href="ArgumentMatcher">ArgumentMatcher类的文档</a>
)。这两种技术都能用于检测外部传递到Mock对象的参数。然而，使用ArgumentCaptor在以下的情况下更合适
:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>自定义不能被重用的参数匹配器</p>
</li>
<li>
<p>你仅需要断言参数值</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>自定义参数匹配器相关的资料你可以参考ArgumentMatcher文档。</p>
</div>
<div class="sect3">
<h4 id="_16_真实的局部mocks_1_8版本之后">16. 真实的局部mocks (1.8版本之后)</h4>
<div class="paragraph">
<p>在内部通过邮件进行了无数争辩和讨论后，最终 Mockito
决定支持部分测试，早前我们不支持是因为我们认为部分测试会让代码变得糟糕。然而，我们发现了部分测试真正合理的用法。http://monkeyisland.pl/2009/01/13/subclass-and-override-vs-partial-mocking-vs-refactoring/[详情点这]</p>
</div>
<div class="paragraph">
<p>在 Mockito 1.8 之前，spy()
方法并不会产生真正的部分测试，而这无疑会让一些开发者困惑。更详细的内容可以看：http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#13[这里]
或
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spy(T)">Java
文档</a></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    //you can create partial mock with spy() method:
    List list = spy(new LinkedList());

    //you can enable partial mock capabilities selectively on mocks:
    Foo mock = mock(Foo.class);
    //Be sure the real implementation is 'safe'.
    //If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
    when(mock.someMethod()).thenCallRealMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>一如既往，你会去读部分测试的警告部分：面向对象编程通过将抽象的复杂度拆分为一个个独立，精确的
SRPy
对象中，降低了抽象处理的复杂度。那部分测试是怎么遵循这个规范的呢？事实上部分测试并没有遵循这个规范……部分测试通常意味着抽象的复杂度被移动到同一个对象的不同方法中，在大多数情况下，这不会是你想要的应用架构方式。</p>
</div>
<div class="paragraph">
<p>然而，在一些罕见的情况下部分测试才会是易用的：处理不能轻易修改的代码（第三方接口，临时重构的遗留代码等等）。然而，为了新的，测试驱动和架构优秀的代码，我是不会使用部分测试的。</p>
</div>
</div>
<div class="sect3">
<h4 id="_17_重置mocks对象_1_8版本之后">17. 重置mocks对象 (1.8版本之后)</h4>
<div class="paragraph">
<p>聪明的 Mockito
使用者很少会用到这个特性，因为他们知道这是出现糟糕测试单元的信号。通常情况下你不会需要重设你的测试单元，只需要为每一个测试方法重新创建一个测试单元就可以了。</p>
</div>
<div class="paragraph">
<p>如果你真的想通过 reset()
方法满足某些需求的话，请考虑实现简单，小而且专注于测试方法而不是冗长，精确的测试。首先可能出现的代码异味就是测试方法中间那的
reset()
方法。这可能意味着你已经过度测试了。请遵循测试方法的呢喃：请让我们小，而且专注于单一的行为上。在
Mockito 邮件列表中就有好几个讨论是和这个有关的。</p>
</div>
<div class="paragraph">
<p>添加 reset() 方法的唯一原因就是让它能与容器注入的测试单元协作。详情看
<a href="http://code.google.com/p/mockito/issues/detail?id=55">issue 55</a> 或
<a href="http://code.google.com/p/mockito/wiki/FAQ">FAQ</a>。</p>
</div>
<div class="paragraph">
<p>别自己给自己找麻烦，reset() 方法在测试方法的中间确实是代码异味。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   List mock = mock(List.class);
   when(mock.size()).thenReturn(10);
   mock.add(1);

   reset(mock);
   //at this point the mock forgot any interactions &amp; stubbing</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_18_故障排查与验证框架的使用_1_8版本之后">18. 故障排查与验证框架的使用 (1.8版本之后)</h4>
<div class="paragraph">
<p>首先，如果出现了任何问题，我建议你先看
<a href="http://code.google.com/p/mockito/wiki/FAQ">Mockito FAQ</a>。</p>
</div>
<div class="paragraph">
<p>任何你提的问题都会被提交到 Mockito
的http://groups.google.com/group/mockito[邮件列表]中。</p>
</div>
<div class="paragraph">
<p>然后你应该知道 Mockito
会验证你是否始终以正确的方式使用它，对此有疑惑的话不妨看看
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#validateMockitoUsage()">validateMockitoUsage()</a>
的文档说明。</p>
</div>
</div>
<div class="sect3">
<h4 id="_19_行为驱动开发的别名_1_8版本之后">19.行为驱动开发的别名 (1.8版本之后)</h4>
<div class="paragraph">
<p>行为驱动开发实现测试单元的模式将 //given //when //then comments
视作测试方法的基础，这也是我们实现单元测试时被建议做的！</p>
</div>
<div class="paragraph">
<p><a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development">你可以在这开始学习有关
BDD 的知识</a></p>
</div>
<div class="paragraph">
<p>问题是当信息没有很好地与 //given //when //then comments
交互时，扮演规范角色的测试桩 API
就会出现问题。这是因为测试桩属于给定测试单元的组件，而且不是任何测试的组件。因此
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/BDDMockito.html">BDDMockito</a>
类介绍了一个别名，使你的测试桩方法调用
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/BDDMockito.html#given(T)">BDDMockito.given(Object)</a>
方法。现在它可以很好地和给定的 BDD 模式的测试单元组件进行交互。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> import static org.mockito.BDDMockito.*;

 Seller seller = mock(Seller.class);
 Shop shop = new Shop(seller);

 public void shouldBuyBread() throws Exception {
   //given
   given(seller.askForBread()).willReturn(new Bread());

   //when
   Goods goods = shop.buyBread();

   //then
   assertThat(goods, containBread());
 }</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_20_序列化mock对象">20. 序列化mock对象</h4>
<div class="paragraph">
<p>模拟对象可以被序列化。有了这个特性你就可以在依赖被序列化的情况下使用模拟对象了。</p>
</div>
<div class="paragraph">
<p>警告：这个特性很少在单元测试中被使用。</p>
</div>
<div class="paragraph">
<p>To create serializable mock use
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockSettings.html#serializable()">MockSettings.serializable()</a>:</p>
</div>
<div class="paragraph">
<p>这个特性通过 BDD 拥有不可考外部依赖的特性的具体用例实现，来自外部依赖的
Web 环境和对象会被序列化，然后在不同层之间被传递。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   List serializableMock = mock(List.class, withSettings().serializable());</code></pre>
</div>
</div>
<div class="paragraph">
<p>The mock can be serialized assuming all the normal
<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/io/Serializable.html">serialization
requirements</a> are met by the class.</p>
</div>
<div class="paragraph">
<p>模拟对象能被序列化假设所有普通的序列化要求都被类满足了。</p>
</div>
<div class="paragraph">
<p>让一个真实的侦查对象可序列化需要多一些努力，因为 spy(…) 方法没有接收
MockSettings 的重载版本。不过不用担心，你几乎不可能用到这。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> List&lt;Object&gt; list = new ArrayList&lt;Object&gt;();
 List&lt;Object&gt; spy = mock(ArrayList.class, withSettings()
                 .spiedInstance(list)
                 .defaultAnswer(CALLS_REAL_METHODS)
                 .serializable());</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_21_新的注解_captor_spy_injectmocks_1_8_3版本之后">21. 新的注解 : @Captor,@Spy,@ InjectMocks (1.8.3版本之后)</h4>
<div class="paragraph">
<p>V1.8.3 带来的新注解在某些场景下可能会很实用</p>
</div>
<div class="paragraph">
<p>@http://site.mockito.org/mockito/docs/current/org/mockito/Captor.html[Captor]
简化
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentCaptor.html">ArgumentCaptor</a>
的创建 -
当需要捕获的参数是一个令人讨厌的通用类，而且你想避免编译时警告。</p>
</div>
<div class="paragraph">
<p>@http://site.mockito.org/mockito/docs/current/org/mockito/Spy.html[Spy]
- 你可以用它代替
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spy(T)">spy(Object)
方法</a></p>
</div>
<div class="paragraph">
<p>@http://site.mockito.org/mockito/docs/current/org/mockito/InjectMocks.html[InjectMocks]
- 自动将模拟对象或侦查域注入到被测试对象中。需要注意的是 @InjectMocks
也能与 @Spy 一起使用，这就意味着 Mockito
会注入模拟对象到测试的部分测试中。它的复杂度也是你应该使用部分测试原因。</p>
</div>
<div class="paragraph">
<p>所有新的注解仅仅在
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockitoAnnotations.html#initMocks(java.lang.Object)">MockitoAnnotations.initMocks(Object)</a>
方法中被处理，就像你在 built-in runner 中使用的
@http://site.mockito.org/mockito/docs/current/org/mockito/Mock.html[Mock]
注解：http://site.mockito.org/mockito/docs/current/org/mockito/runners/MockitoJUnitRunner.html[MockitoJUnitRunner]
或 规范:
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/junit/MockitoRule.html">MockitoRule</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_22_验证超时_1_8_5版本之后">22. 验证超时 (1.8.5版本之后)</h4>
<div class="paragraph">
<p>允许带有暂停的验证。这使得一个验证去等待一段特定的时间，以获得想要的交互而不是如果还没有发生事件就带来的立即失败。在并发条件下的测试这会很有用。</p>
</div>
<div class="paragraph">
<p>感觉起来这个特性应该很少被使用 - 指出更好的测试多线程系统的方法。</p>
</div>
<div class="paragraph">
<p>还没有实现去和 InOrder 验证协作。</p>
</div>
<div class="paragraph">
<p>例子：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   //passes when someMethod() is called within given time span
   verify(mock, timeout(100)).someMethod();
   //above is an alias to:
   verify(mock, timeout(100).times(1)).someMethod();

   //passes when someMethod() is called *exactly* 2 times within given time span
   verify(mock, timeout(100).times(2)).someMethod();

   //passes when someMethod() is called *at least* 2 times within given time span
   verify(mock, timeout(100).atLeast(2)).someMethod();

   //verifies someMethod() within given time span using given verification mode
   //useful only if you have your own custom verification modes.
   verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_23_自动初始化被_spies_injectmocks注解的字段以及构造函数注入_1_9_0版本之后">23. 自动初始化被@Spies, @InjectMocks注解的字段以及构造函数注入 (1.9.0版本之后)</h4>
<div class="paragraph">
<p>Mockito 现在会通过注入构造方法、setter 或域注入尽可能初始化带有
@http://site.mockito.org/mockito/docs/current/org/mockito/Spy.html[Spy]
和
@http://site.mockito.org/mockito/docs/current/org/mockito/InjectMocks.html[InjectMocks]
注解的域或方法。</p>
</div>
<div class="paragraph">
<p>为了利用这一点特性，你需要使用
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockitoAnnotations.html#initMocks(java.lang.Object)">MockitoAnnotations.initMocks(Object)</a>,
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/runners/MockitoJUnitRunner.html">MockitoJUnitRunner</a>
或
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/junit/MockitoRule.html">MockitoRule</a>。</p>
</div>
<div class="paragraph">
<p>为了 InjectMocks 请在 Java 文档中了解更多可用的技巧和注入的规范</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //instead:
 @Spy BeerDrinker drinker = new BeerDrinker();
 //you can write:
 @Spy BeerDrinker drinker;

 //same applies to @InjectMocks annotation:
 @InjectMocks LocalPub;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_24_单行测试桩_1_9_0版本之后">24. 单行测试桩 (1.9.0版本之后)</h4>
<div class="paragraph">
<p>Mockito
现在允许你在使用测试桩时创建模拟对象。基本上，它允许在一行代码中创建一个测试桩，这对保持代码的整洁很有用。举例来说，有些乏味的测试桩会被创建，并在测试初始化域时被打入，例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> public class CarTest {
   Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();

   @Test public void should... {}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_25_验证被忽略的测试桩_1_9_0版本之后">25. 验证被忽略的测试桩 (1.9.0版本之后)</h4>
<div class="paragraph">
<p>Mockito 现在允许为了验证无视测试桩。在与 verifyNoMoreInteractions()
方法或验证 inOrder()
方法耦合时，有些时候会很有用。帮助避免繁琐的打入测试桩调用验证 -
显然我们不会对验证测试桩感兴趣。</p>
</div>
<div class="paragraph">
<p>警告，ignoreStubs() 可能会导致 verifyNoMoreInteractions(ignoreStubs(…))
的过度使用。谨记在心，Mockito 没有推荐用
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#verifyNoMoreInteractions(java.lang.Object&#8230;&#8203;)">verifyNoMoreInteractions()</a>
方法连续地施用于每一个测试中，原因在 Java 文档中有。</p>
</div>
<div class="paragraph">
<p>一些例子：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> verify(mock).foo();
 verify(mockTwo).bar();

 //ignores all stubbed methods:
 verifyNoMoreInvocations(ignoreStubs(mock, mockTwo));

 //creates InOrder that will ignore stubbed
 InOrder inOrder = inOrder(ignoreStubs(mock, mockTwo));
 inOrder.verify(mock).foo();
 inOrder.verify(mockTwo).bar();
 inOrder.verifyNoMoreInteractions();</code></pre>
</div>
</div>
<div class="paragraph">
<p>更好的例子和更多的细节都可以在 Java 文档的
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#ignoreStubs(java.lang.Object&#8230;&#8203;)">ignoreStubs(Object…)</a>
部分看到。</p>
</div>
</div>
<div class="sect3">
<h4 id="_26_mock详情_1_9_5版本之后">26. mock详情 (1.9.5版本之后)</h4>
<div class="paragraph">
<p>为了区别一个对象是模拟对象还是侦查对象：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">     Mockito.mockingDetails(someObject).isMock();
     Mockito.mockingDetails(someObject).isSpy();</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockingDetails.html#isMock()">MockingDetails.isMock()</a>
和
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockingDetails.html#isSpy()">MockingDetails.isSpy()</a>
方法都会返回一个布尔值。因为一个侦查对象只是模拟对象的一种变种，所以
isMock() 方法在对象是侦查对象是会返回 true。在之后的 Mockito 版本中
MockingDetails
会变得更健壮，并提供其他与模拟对象相关的有用信息，例如：调用，测试桩信息，等等……</p>
</div>
</div>
<div class="sect3">
<h4 id="委托调用真实实例delegating_call_to_real_instance-since-1.9.5">27. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#delegating_call_to_real_instance">委托调用真实实例</a> (Since 1.9.5)</h4>
<div class="paragraph">
<p>当<strong>使用常规的 spy API 去 mock 或者 spy 一个对象很困难</strong>时可以用
delegate 来 spy 或者 mock 对象的某一部分。 从 Mockito 的 1.10.11
版本开始， delegate 有可能和 mock
的类型相同也可能不同。如果不是同一类型， delegate
类型需要提供一个匹配方法否则就会抛出一个异常。下面是关于这个特性的一些用例:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>带有 interface 的 final 类</p>
</li>
<li>
<p>已经自定义代理的对象</p>
</li>
<li>
<p>带有 finalize 方法的特殊对象，就是避免重复执行。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>和常规 spy 的不同:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>标准的 spy
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spy(T)">(spy(Object))</a>
包含被 spy 实例的所有状态信息，方法在 spy 对象上被调用。被 spy
的对象只在 mock 创建时被用来拷贝状态信息。如果你通过标准 spy
调用一个方法，这个 spy 会调用其内部的其他方法记录这次操作，
以便后面验证使用。等效于存根 (stubbed)操作。</p>
</li>
<li>
<p>mock delegates 只是简单的把所有方法委托给 delegate。delegate
一直被当成它代理的方法使用。如果你 从一个 mock
调用它被委托的方法，它会调用其内部方法，这些调用不会被记录，stubbing
在这里也不会生效。 Mock 的 delegates 相对于标准的 spy
来说功能弱了很多，不过在标准 spy 不能被创建的时候很有用。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>更多信息可以看这里
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/AdditionalAnswers.html#delegatesTo(java.lang.Object)">AdditionalAnswers.delegatesTo(Object)</a>.</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="mockmaker-api-mock_maker_pluginsince-1.9.5">28. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#mock_maker_plugin">MockMaker API</a>(Since 1.9.5)</h4>
<div class="paragraph">
<p>为了满足用户的需求和 Android 平台使用。Mockito
现在提供一个扩展点，允许替换代理生成引擎。默认情况下，Mockito 使用 cglib
创建动态代理。</p>
</div>
<div class="paragraph">
<p>这个扩展点是为想要扩展 Mockito
功能的高级用户准备的。比如，我们现在就可以在 dexmaker 的帮助下使用
Mockito 测试 Android。</p>
</div>
<div class="paragraph">
<p>更多的细节，原因和示例请看
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/plugins/MockMaker.html">MockMaker</a>
的文档。</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-bdd-风格的验证bdd_behavior_verification-since-1.10.0">29. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#BDD_behavior_verification">(new) BDD 风格的验证</a> (Since 1.10.0)</h4>
<div class="paragraph">
<p>开启 Behavior Driven Development (BDD) 风格的验证可以通过 BBD 的关键词
<strong>then</strong> 开始验证。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> given(dog.bark()).willReturn(2);

 // when
 ...

 then(person).should(times(2)).ride(bike);</code></pre>
</div>
</div>
<div class="paragraph">
<p>更多信息请查阅
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/BDDMockito.html#then(T)">BDDMockito.then(Object)</a>
.</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-spying-或-mocking-抽象类spying_abstract_classes-since-1.10.12">30. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spying_abstract_classes">(new) Spying 或 mocking 抽象类</a> (Since 1.10.12)</h4>
<div class="paragraph">
<p>现在可以方便的 spy 一个抽象类。注意，过度使用 spy
或许意味着代码的设计上有问题。(see
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spy(T)">spy(Object)</a>).</p>
</div>
<div class="paragraph">
<p>之前，spying 只可以用在实例对象上。而现在新的 API 可以在创建一个 mock
实例时使用构造函数。这对 mock
一个抽象类来说是很重要的，这样使用者就不必再提供一个抽象类的实例了。目前的话只支持无参构造函数，
如果你认为这样还不够的话欢迎向我们反馈。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">//convenience API, new overloaded spy() method:
 SomeAbstract spy = spy(SomeAbstract.class);

 //Robust API, via settings builder:
 OtherAbstract spy = mock(OtherAbstract.class, withSettings()
    .useConstructor().defaultAnswer(CALLS_REAL_METHODS));

 //Mocking a non-static inner abstract class:
 InnerAbstract spy = mock(InnerAbstract.class, withSettings()
    .useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));</code></pre>
</div>
</div>
<div class="paragraph">
<p>更多信息请见
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockSettings.html#useConstructor()">MockSettings.useConstructor()</a>
.</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-mockito-mocks-可以通过-classloaders-序列化反序列化serilization_across_classloader-since-1.10.0">31. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#serilization_across_classloader">(new) Mockito mocks 可以通过 classloaders 序列化/反序列化</a> (Since 1.10.0)</h4>
<div class="paragraph">
<p>Mockito 通过 classloader 引入序列化。和其他形式的序列化一样，所有 mock
层的对象都要被序列化， 包括
answers。因为序列化模式需要大量的工作，所以这是一个可选择设置。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">// 常规的 serialization
mock(Book.class, withSettings().serializable());

// 通过 classloaders 序列化
mock(Book.class, withSettings().serializable(ACROSS_CLASSLOADERS));</code></pre>
</div>
</div>
<div class="paragraph">
<p>更多信息请查看
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockSettings.html#serializable(org.mockito.mock.SerializableMode)">MockSettings.serializable(SerializableMode)</a>.</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-deep-stubs-更好的泛型支持better_generic_support_with_deep_stubs-since-1.10.0">32. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#better_generic_support_with_deep_stubs">(new) Deep stubs 更好的泛型支持</a> (Since 1.10.0)</h4>
<div class="paragraph">
<p>Deep stubbing 现在可以更好的查找类的泛型信息。这就意味着像这样的类
不必去 mock 它的行为就可以使用。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">class Lines extends List&lt;Line&gt; {
     // ...
 }

 lines = mock(Lines.class, RETURNS_DEEP_STUBS);

 // Now Mockito understand this is not an Object but a Line
 Line line = lines.iterator().next();</code></pre>
</div>
</div>
<div class="paragraph">
<p>请注意，大多数情况下 mock 返回一个 mock 对象是错误的。</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-mockito-junit-rulemockito_junit_rule-since-1.10.17">33. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#mockito_junit_rule">(new) Mockito JUnit rule</a> (Since 1.10.17)</h4>
<div class="paragraph">
<p>Mockito 现在提供一个 JUnit rule。目前为止，有两种方法可以初始化 fields
，使用 Mockito 提供的注解比如
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mock.html">@Mock</a>,
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Spy.html">@Spy</a>,
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/InjectMocks.html">@InjectMocks</a>
等等。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>用
@RunWith(<a href="http://site.mockito.org/mockito/docs/current/org/mockito/runners/MockitoJUnitRunner.html">@MockitoJUnitRunner.class</a>)
标注 JUnit 测试类</p>
</li>
<li>
<p>在 @Before 之前调用
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockitoAnnotations.html#initMocks(java.lang.Object)">MockitoAnnotations.initMocks(Object)</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>现在你可以选择使用一个 rule:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> @RunWith(YetAnotherRunner.class)
 public class TheTest {
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
     // ...
 }</code></pre>
</div>
</div>
<div class="paragraph">
<p>更多信息到这里查看
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/junit/MockitoJUnit.html#rule()">MockitoJUnit.rule()</a>.</p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="new-开启和关闭-pluginspluginswitch-since-1.10.15">34. <a href="http://site.mockito.org/mockito/docs/current/org/mockito/plugins/PluginSwitch.html">(new) 开启和关闭 plugins</a> (Since 1.10.15)</h4>
<div class="paragraph">
<p>这是一个测试特性，可以控制一个 mockito-plugin 开启或者关闭。详情请查看
<a href="http://site.mockito.org/mockito/docs/current/org/mockito/plugins/PluginSwitch.html">PluginSwitch</a></p>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_35_自定义验证失败信息_since_2_0_0">35. 自定义验证失败信息 (Since 2.0.0)</h4>
<div class="paragraph">
<p>允许声明一个在验证失败时输出的自定义消息 示例:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> // will print a custom message on verification failure
 verify(mock, description("This will print on failure")).someMethod();

 // will work with any verification mode
 verify(mock, times(2).description("someMethod should be called twice")).someMethod();</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_字段摘要">字段摘要</h4>
<div class="paragraph">
<p>类型</p>
</div>
<div class="paragraph">
<p>字段以及描述</p>
</div>
<div class="paragraph">
<p>static Answer&lt; Object &gt;</p>
</div>
<div class="paragraph">
<p>CALLS_REAL_METHODS 用于mock(Class, Answer)的可选参数Answer</p>
</div>
</div>
<div class="sect3">
<h4 id="_字段详情">字段详情</h4>
<div class="paragraph">
<p><strong>CALLS_REAL_METHODS</strong></p>
</div>
<div class="paragraph">
<p>public static final Answer CALLS_REAL_METHODS 用于mock(Class,
Answer)的可选参数Answer Answer可以用于定义unstubbed invocations的返回值.</p>
</div>
<div class="paragraph">
<p>这个Answer接口对于legacy code非常有用. 当使用这个接口的时候, unstubbed
methods会被实现. 这是一种通过调用默认方法来创建partial mock对象的方式。</p>
</div>
<div class="paragraph">
<p>通常，你将要阅读mock的部分警告：Object oriented programming is more less
tackling complexity by dividing the complexity into separate, specific,
, SRPy objects. partial
mock是如果适应这种模式的呢？好吧！它不仅仅是，partial
mock通常意味着复杂性在同一个对象中移动到不同的方法.在大多数情况下，这不是你想要的设计你的应用的方式。</p>
</div>
<div class="paragraph">
<p>然而，当partial
mocks派上用场同样也有少许情况:处理你不易改变的代码（第三方接口，legacy
code的临时重构）.我将不使用partial
mocks用于新的、测试驱动以及设计不错的代码。 例如：</p>
</div>
<div class="paragraph">
<p>Foo mock = mock(Foo.class, CALLS_REAL_METHODS); // this calls the real
implementation of Foo.getSomething() value = mock.getSomething();
when(mock.getSomething()).thenReturn(fakeValue); // now fakeValue is
returned value = mock.getSomething();</p>
</div>
</div>
<div class="sect3">
<h4 id="_方法摘要">方法摘要</h4>
<div class="paragraph">
<p>Modifier and Type</p>
</div>
<div class="paragraph">
<p>Method and Description</p>
</div>
<div class="paragraph">
<p>static VerificationAfterDelay</p>
</div>
<div class="paragraph">
<p>after(long millis) 给定的时间后进行验证</p>
</div>
<div class="paragraph">
<p>static VerificationMode</p>
</div>
<div class="paragraph">
<p>atLeast(int minNumberOfInvocations) 至少进行minNumberOfInvocations次验证</p>
</div>
<div class="paragraph">
<p>static VerificationMode</p>
</div>
<div class="paragraph">
<p>atLeastOnce() 至少进行一次验证</p>
</div>
<div class="paragraph">
<p>static VerificationMode</p>
</div>
<div class="paragraph">
<p>atMost(int maxNumberOfInvocations) 最多进行maxNumberOfInvocations次验证</p>
</div>
<div class="paragraph">
<p>static VerificationMode</p>
</div>
<div class="paragraph">
<p>calls(int wantedNumberOfInvocations) 允许顺序进行non-greedy验证</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_方法详情">52. 方法详情</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_after">52.1. after</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static VerificationAfterDelay after(long millis)</code></pre>
</div>
</div>
<div class="paragraph">
<p>在给定的时间后进行验证。它会为了预期的效果进行等待一段时间后进行验证，而不是因为没发生而立即失败。这可能对于测试多并发条件非常有用。</p>
</div>
<div class="paragraph">
<p>after()等待整个周期的特点不同于timeout()，而timeout()一旦验证通过就尽快停止，例如：当使用times(2)可以产生不同的行为方式，可能通过后过会又失败。这种情况下，timeout只要times(2)通过就会通过，然后after执行完整个周期时间，可能会失败，也意味着times(2)也失败。</p>
</div>
<div class="paragraph">
<p>感觉这个方法应该少使用——找到更好的方法测试你的多线程系统。</p>
</div>
<div class="paragraph">
<p>对尚未实现的工作进行验证。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    //passes after 100ms, if someMethod() has only been called once at that time.&lt;br&gt;
    verify(mock, after(100)).someMethod();&lt;br&gt;
    //above is an alias to:&lt;br&gt;
    verify(mock, after(100).times(1)).someMethod();

    //passes if someMethod() is called *exactly* 2 times after the given timespan
    verify(mock, after(100).times(2)).someMethod();

    //passes if someMethod() has not been called after the given timespan&lt;br&gt;
    verify(mock, after(100).never()).someMethod();

    //verifies someMethod() after a given time span using given verification mode
    //useful only if you have your own custom verification modes.
    verify(mock, new After(100, yourOwnVerificationMode)).someMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照Mockito类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>millis - - time span in milliseconds</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_atleast">52.2. atLeast</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static VerificationMode atLeast(int minNumberOfInvocations)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许至少进行x次验证。例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    verify(mock, atLeast(3)).someMethod("some arg");</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照Mockito类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>minNumberOfInvocations - invocations的最小次数</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_atleastonce">52.3. atLeastOnce</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static VerificationMode atLeastOnce()</code></pre>
</div>
</div>
<div class="paragraph">
<p>至少进行一次一次验证。例如:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    verify(mock, atLeastOnce()).someMethod("some arg");</code></pre>
</div>
</div>
<div class="paragraph">
<p>atLeast(1)的别名. 参照Mockito类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_atmost">52.4. atMost</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static VerificationMode atMost(int maxNumberOfInvocations)</code></pre>
</div>
</div>
<div class="paragraph">
<p>至多进行x次验证. 例如:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    verify(mock, atMost(3)).someMethod("some arg");</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照Mockito类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters::</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>maxNumberOfInvocations - invocations的最大次数</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_calls">52.5. calls</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static VerificationMode calls(int wantedNumberOfInvocations)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许顺序进行non-greedy验证. 例如:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    inOrder.verify( mock, calls( 2 )).someMethod( "some arg" );</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果这个方法调用3次不会失败，不同于times(2)</p>
</div>
<div class="paragraph">
<p>不会标记第三次验证，不同于atLeast(2)</p>
</div>
<div class="paragraph">
<p>这个verification mode只能用于顺序验证.</p>
</div>
<div class="paragraph">
<p><strong>Parameters::</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>wantedNumberOfInvocations - 验证的次数</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_继承org_mockito_matchers的方法">53. 继承org.mockito.Matchers的方法</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_any">53.1. any</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static &lt;T&gt; T any()&lt;br&gt;&lt;br&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>匹配任何值，包括null</p>
</div>
<div class="paragraph">
<p>anyObject()的别名</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p>这是: anyObject() and any(java.lang.Class)的别名</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>null</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_any_2">53.2. any</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static &lt;T&gt; T any(Class&lt;T&gt; clazz)</code></pre>
</div>
</div>
<div class="paragraph">
<p>匹配任何对象，包括null</p>
</div>
<div class="paragraph">
<p>这个方法不进行给定参数的类型检查，它就是为了避免代码中的强制转换(Casting)。然而这可能会改变（类型检查可以添加）将来的主要版本。</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p>这是: anyObject() and any(java.lang.Class)的别名</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>null ## anyBoolean</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static boolean anyBoolean()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何boolean类型或非空(non-null)的Boolean.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>false</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anybyte">53.3. anyByte</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static byte anyByte()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何byte类型变量或非空(non-null)Byte.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>0</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anychar">53.4. anyChar</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static char anyChar()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何char类型变量或非空(non-null)的Character.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>0</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anycollection">53.5. anyCollection</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static Collection anyCollection()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何非空(non-null)的Collection.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Collection.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anycollectionof">53.6. anyCollectionOf</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; T &gt; Collection &lt; T &gt; anyCollectionOf(Class&lt;T&gt; clazz)</code></pre>
</div>
</div>
<div class="paragraph">
<p>通用友好的别名anyCollection()。为了保持代码清洁，通过@SuppressWarnings(&#8220;unchecked&#8221;)来进行替代编译器警告。</p>
</div>
<div class="paragraph">
<p>任何非空(non-null)Collection.</p>
</div>
<div class="paragraph">
<p>这个方法不进行给定参数的类型检查，它就是为了避免代码中的强制转换(Casting)。然而这可能会改变（类型检查可以添加）将来的主要版本。</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>clazz - 类型属于Collection类型避免类型转换(Casting)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Collection.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anydouble">53.7. anyDouble</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static double anyDouble()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何double类型或非空(non-null)的Double.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyfloat">53.8. anyFloat</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static float anyFloat()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何float类型或非空(non-null)Float.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyint">53.9. anyInt</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static int anyInt()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何int或非空(non-null)Integer.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anylist">53.10. anyList</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static List anyList()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何非空(non-null)List.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空List.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anylistof">53.11. anyListOf</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; T &gt;  List &lt; T &gt; anyListOf(Class&lt; T &gt; clazz)</code></pre>
</div>
</div>
<div class="paragraph">
<p>通用友好的别名anyList()。为了保持代码清洁，通过@SuppressWarnings(&#8220;unchecked&#8221;)来进行替代编译器警告。</p>
</div>
<div class="paragraph">
<p>任何非空(non-null)List.</p>
</div>
<div class="paragraph">
<p>这个方法不进行给定参数的类型检查，它就是为了避免代码中的强制转换(Casting)。然而这可能会改变（类型检查可以添加）将来的主要版本。</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>clazz - 类型属于List类型避免类型转换(Casting)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空List.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anylong">53.12. anyLong</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static long anyLong()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何long类型或非空(non-null)Long.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anymap">53.13. anyMap</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static Map anyMap()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何非空(non-null)Map.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Map.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anymapof">53.14. anyMapOf</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; K,V&gt; Map &lt; K,V&gt; anyMapOf(Class&lt; K&gt; keyClazz, Class&lt; V&gt; valueClazz)</code></pre>
</div>
</div>
<div class="paragraph">
<p>通用友好的别名anyMap()。为了保持代码清洁，通过@SuppressWarnings(&#8220;unchecked&#8221;)来进行替代编译器警告。</p>
</div>
<div class="paragraph">
<p>任何非空(non-null)Map.</p>
</div>
<div class="paragraph">
<p>这个方法不进行给定参数的类型检查，它就是为了避免代码中的强制转换(Casting)。然而这可能会改变（类型检查可以添加）将来的主要版本。</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>keyClazz - map key类型避免类型强制转换(Casting)</p>
</li>
<li>
<p>valueClazz - value类型避免类型强制转换(Casting)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Map.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyobject">53.15. anyObject</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; T&gt; T anyObject()</code></pre>
</div>
</div>
<div class="paragraph">
<p>匹配任何事物, 包含null.</p>
</div>
<div class="paragraph">
<p>这是: any()和any(java.lang.Class)的别名</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>empty null.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyset">53.16. anySet</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt;a href="http://docs.oracle.com/javase/8/docs/api/java/util/Set.html?is-external=true"&gt;Set&lt;/a&gt; anySet()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何非空(non-null)Set.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Set.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anysetof">53.17. anySetOf</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; T&gt; Set &lt; T&gt; anySetOf(Class&lt; T&gt; clazz)</code></pre>
</div>
</div>
<div class="paragraph">
<p>通用友好的别名anySet()。为了保持代码清洁，通过@SuppressWarnings(&#8220;unchecked&#8221;)来进行替代编译器警告。</p>
</div>
<div class="paragraph">
<p>任何非空(non-null)Set.</p>
</div>
<div class="paragraph">
<p>这个方法不进行给定参数的类型检查，它就是为了避免代码中的强制转换(Casting)。然而这可能会改变（类型检查可以添加）将来的主要版本。</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>clazz - 类型属于Set为了避免类型强制转换(Casting)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空Set.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyshort">53.18. anyShort</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static short anyShort()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何short类型或非空(non-null)Short.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anystring">53.19. anyString</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static String anyString()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何非空(non-null)String</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空String (““).</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_anyvararg">53.20. anyVararg</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static &lt; T&gt; T anyVararg()</code></pre>
</div>
</div>
<div class="paragraph">
<p>任何vararg类型, 即任何参数(arguments)的number和values</p>
</div>
<div class="paragraph">
<p>例如:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    //verification:
    mock.foo(1, 2);
    mock.foo(1, 2, 3, 4);
    verify(mock, times(2)).foo(anyVararg());

    //stubbing:
    when(mock.foo(anyVararg()).thenReturn(100);

    //prints 100
    System.out.println(mock.foo(1, 2));
    //also prints 100&lt;
    System.out.println(mock.foo(1, 2, 3, 4));</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>null.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_argthat">53.21. argThat</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static &lt; T&gt; T argThat(ArgumentMatcher &lt; T&gt; matcher)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许创建自定义的参数匹配模式.这个API在2.0中已经改变,请阅读ArgumentMatcher基础指南。</p>
</div>
<div class="paragraph">
<p>在实现自定义参数匹配模式前，理解使用的场景和non-trivial参数的可用选项是非常重要的。这种方式下，你可以在给定的情况下选择最好的方法来设计制造高质量的测试(清洁和维护).请阅读ArgumentMatcher文档学习方法和例子。</p>
</div>
<div class="paragraph">
<p>在极少数情况下，当参数是基本数据类型(primitive)时，你必须使用相关的intThat()、floatThat()等方法。这些方法在进行自动拆箱(auto-unboxing)时可以避免NullPointerException异常。</p>
</div>
<div class="paragraph">
<p>参照ArgumentMatcher类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>matcher - 取决于选择的参数匹配模式(argument matches)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>null.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_booleanthat">53.22. booleanThat</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static boolean booleanThat(ArgumentMatcher &lt; Boolean&gt; matcher)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许创建自定义的Boolean类型参数匹配模式(Boolean argument matchers).</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>matcher - 取决于选择的参数匹配模式(argument matches) <strong>Returns:</strong></p>
</li>
<li>
<p>false.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_bytethat">53.23. byteThat</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static byte byteThat(ArgumentMatcher &lt; Byte&gt; matcher)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许创建自定义的Byte类型参数匹配模式(Byte argument matchers)</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>matcher - 取决于选择的参数匹配模式(argument matches)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_charthat">53.24. charThat</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">public static char charThat(ArgumentMatcher &lt; Character&gt; matcher)</code></pre>
</div>
</div>
<div class="paragraph">
<p>允许创建自定义的Character类型参数匹配模式(Character argument matchers)</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>matcher - 取决于选择的参数匹配模式(argument matches)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p></p>
<div class="olist arabic">
<ol class="arabic" start="0">
<li>
<p></p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_contains">53.25. contains</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">    public static String contains(String substring)</code></pre>
</div>
</div>
<div class="paragraph">
<p>String参数包含给定的substring字符串.</p>
</div>
<div class="paragraph">
<p>参照Matchers类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>substring - substring字符串.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>空String (““).</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_description函数">53.26. description函数</h3>
<div class="paragraph">
<p><code>public static VerificationMod description(String description)</code></p>
</div>
<div class="paragraph">
<p>添加验证失败时要输出的文字内容</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">verify(mock, description("This will print on failure")).someMethod("some arg");</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>输出的文字内容</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>验证模式</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>2.0.0</p>
</li>
</ul>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_doanswer函数">53.27. doAnswer函数</h3>
<div class="paragraph">
<p><code>public static Stubber doAnswer(Answer answer)</code></p>
</div>
<div class="paragraph">
<p>当你想要测试一个无返回值的函数时，可以使用一个含有泛型类http://site.mockito.org/mockito/docs/current/org/mockito/stubbing/Answer.html[Answer]参数的doAnswer()函数。为无返回值的函数做测试桩与`when(Objecy)`方法不同，因为编译器不喜欢在大括号内使用void函数。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doAnswer(new Answer() {
      public Object answer(InvocationOnMock invocation) {
          Object[] args = invocation.getArguments();
          Mock mock = invocation.getMock();
          return null;
      }})
  .when(mock).someMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>测试函数的应答内容</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>测试方法的测试桩</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_docallrealmethod函数">53.28. doCallRealMethod函数</h3>
<div class="paragraph">
<p><code>public static Stubber doCallRealMethod()</code></p>
</div>
<div class="paragraph">
<p>如果你想调用某一个方法的真实实现请使用`doCallRealMethod()`。</p>
</div>
<div class="paragraph">
<p>像往常一样你需要阅读<strong>局部的mock对象警告</strong>:面向对象编程通过将复杂的事物分解成单独的、具体的、SRPY对象来减少对复杂事件的处理。
局部模拟是如何符合这种范式的呢。？局部模拟通常情况下是指在对象相同的情况下那些复杂的事物被移动另一个不同的方法中。在大多数情况下，并没有按照你所希望的方式来设计你的应用。</p>
</div>
<div class="paragraph">
<p>然而，使用局部mock对象也会有个别情况:有些代码你并不能非常容易的改变(3rd接口,临时遗留代码的重构等)，但是我对于新的、测试驱动及良好设计的代码不会使用局部mock对象。</p>
</div>
<div class="paragraph">
<p>同样在javadoc中`spy(Object)<code>阅读更多关于partial
mocks的说明.<strong>推荐使用</code>Mockito.spy()<code>来创建局部mock对象</strong>原因是由于你负责构建对象并传值到</code>spy()`中，它只管保证被调用。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">Foo mock = mock(Foo.class);
   doCallRealMethod().when(mock).someVoidMethod();

   // this will call the real implementation of Foo.someVoidMethod()
   // 调用Foo.someVoidMethod()的真实现
   mock.someVoidMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>测试方法的测试桩</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>1.9.5</p>
</li>
</ul>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_donothing函数">53.29. doNothing函数</h3>
<div class="paragraph">
<p><code>public static Stubber doNothing()</code></p>
</div>
<div class="paragraph">
<p>使用`doNothing()`函数是为了设置void函数什么也不做。<strong>需要注意的是默认情况下返回值为void的函数在mocks中是什么也不做的</strong>但是，也会有一些特殊情况。</p>
</div>
<div class="paragraph">
<p>1.测试桩连续调用一个void函数</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doNothing().
   doThrow(new RuntimeException())
   .when(mock).someVoidMethod();

   //does nothing the first time:
   //第一次才能都没做
   mock.someVoidMethod();

   //throws RuntimeException the next time:
   //一下次抛出RuntimeException
   mock.someVoidMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>2.当你监控真实的对象并且你想让void函数什么也不做：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">List list = new LinkedList();
   List spy = spy(list);

   //let's make clear() do nothing
   doNothing().when(spy).clear();

   spy.add("one");

   //clear() does nothing, so the list still contains "one"
   spy.clear();</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>stubber - 测试方法的测试桩</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_doreturn函数">53.30. doReturn函数</h3>
<div class="paragraph">
<p><code>public static Stubber doReturn(Object toBeReturned)</code></p>
</div>
<div class="paragraph">
<p>在某些特殊情况下如果你无法使用`when(Object)<code>可以使用</code>doReturn()`函数</p>
</div>
<div class="paragraph">
<p><strong>注意：对于测试桩推荐使用`when(Object)`函数，因为它是类型安全的并且可读性更强</strong>(特别是在测试桩连续调用的情况下)</p>
</div>
<div class="paragraph">
<p>都有哪些特殊情况下需要使用`doReturn()`</p>
</div>
<div class="paragraph">
<p>1.当监控真实的对象并且调用真实的函数带来的影响时：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">List list = new LinkedList();
   List spy = spy(list);

   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)

   // 不能完成的：真实方法被调用所以spy.get(0)抛出IndexOutOfBoundsException(list仍是空的)
   when(spy.get(0)).thenReturn("foo");

   //You have to use doReturn() for stubbing:
   //你应用使用doReturn()函数
   doReturn("foo").when(spy).get(0);</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic" start="2">
<li>
<p>重写一个前exception-stubbing：</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">when(mock.foo()).thenThrow(new RuntimeException());

   //Impossible: the exception-stubbed foo() method is called so RuntimeException is thrown.
    // 不能完成的：exception-stubbed foo()被调用抛出RuntimeException异常
   when(mock.foo()).thenReturn("bar");

   //You have to use doReturn() for stubbing:
   //你应用使用doReturn()函数
   doReturn("bar").when(mock).foo();</code></pre>
</div>
</div>
<div class="paragraph">
<p>上面的情况展示了Mockito’s的优雅语法。注意这些情况并不常见。监控应该是分散的并且重写exception-stubbing也不常见。更何况对于指出测试桩并复写测试桩是一种潜在的代码嗅觉</p>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>toBeReturned - 当测试桩函数被调用时要被返回的对象</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>stubber - 测试方法的测试桩</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_dothrow函数">53.31. doThrow函数</h3>
<div class="paragraph">
<p><code>public static Stubber doThrow(Class&lt;? extends Throwable&gt; toBeThrown)</code></p>
</div>
<div class="paragraph">
<p>当你想测试void函数中指定类的抛出异常时使用`doThrow()`</p>
</div>
<div class="paragraph">
<p>当每一个函数被调用时一个新的异常实例将会被创建</p>
</div>
<div class="paragraph">
<p>为无返回值的函数做测试桩与`when(Objecy)`方法不同，因为编译器不喜欢在大括号内使用void函数。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doThrow(RuntimeException.class).when(mock).someVoidMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>测试方法被调用时返回的对象</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>测试方法的测试桩</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>1.9.0</p>
</li>
</ul>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_dothrow函数_2">53.32. doThrow函数</h3>
<div class="paragraph">
<p><code>public static Stubber doThrow(Throwable toBeThrown)</code></p>
</div>
<div class="paragraph">
<p>当测试一个void函数的异常时使用`doThrow()`</p>
</div>
<div class="paragraph">
<p>测试void函数需要与使用`when(Object)`不同的方式，因为编译器不喜欢大括号内有void函数</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">doThrow(RuntimeException.class).when(mock).someVoidMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>测试方法被调用时返回的对象</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>测试方法的测试桩</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>1.9.0</p>
</li>
</ul>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_ignorestubs函数">53.33. ignoreStubs函数</h3>
<div class="paragraph">
<p><code>public static Object[] ignoreStubs(Object... mocks)</code></p>
</div>
<div class="paragraph">
<p>忽略对验证函数的测试，当与`verifyNoMoreInteractions()`成对出现或是验证inOrder()时是很有用的。避免了在测试时的多余验证，实际上我们对验证测试一点也不感兴趣。</p>
</div>
<div class="paragraph">
<p><strong>警告</strong>,<code>ignoreStubs()`可能会导致</code>+verifyNoMoreInteractions(ignoreStubs(&#8230;&#8203;))`的过度使用。考虑到Mockito并不推荐使用`+verifyNoMoreInteractions()<code>函数轰炸每一个测试，这其中的原由在文档</code>verifyNoMoreInteractions(Object…)`部分已经说明：换句话说在mocks中所有*
<strong>stubbed</strong> * 的函数都被标记为 * <strong>verified</strong> * 所以不需要使用这种方式。</p>
</div>
<div class="paragraph">
<p>该方法改变了input mocks！该方法只是为了方便返回 imput mocks 。</p>
</div>
<div class="paragraph">
<p>忽略测试也会被忽略掉验证inOrder,包括`InOrder.verifyNoMoreInteractions()`，看下面第二个示例：</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">//mocking lists for the sake of the example (if you mock List in real you will burn in hell)
  List mock1 = mock(List.class), mock2 = mock(List.class);

  //stubbing mocks:
  when(mock1.get(0)).thenReturn(10);
  when(mock2.get(0)).thenReturn(20);

  //using mocks by calling stubbed get(0) methods:

  // 调用stubbed get(0)使用mocks
  System.out.println(mock1.get(0)); //prints 10
  System.out.println(mock2.get(0)); //prints 20

  //using mocks by calling clear() methods:
  // 调用clear()使用mocks
  mock1.clear();
  mock2.clear();

  //verification:

  // 验证
  verify(mock1).clear();
  verify(mock2).clear();

  //verifyNoMoreInteractions() fails because get() methods were not accounted for.

  // verifyNoMoreInteractions()会失败，因为get()未关联账号

  try { verifyNoMoreInteractions(mock1, mock2); } catch (NoInteractionsWanted e);

  //However, if we ignore stubbed methods then we can verifyNoMoreInteractions()
  //如要我们忽略测试函数我可以这样verifyNoMoreInteractions()

  verifyNoMoreInteractions(ignoreStubs(mock1, mock2));

  //Remember that ignoreStubs() *changes* the input mocks and returns them for convenience.</code></pre>
</div>
</div>
<div class="paragraph">
<p>忽略测试可以用于<strong>verification in order</strong>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">List list = mock(List.class);
  when(mock.get(0)).thenReturn("foo");

  list.add(0);
  System.out.println(list.get(0)); //we don't want to verify this
  list.clear();

  InOrder inOrder = inOrder(ignoreStubs(list));
  inOrder.verify(list).add(0);
  inOrder.verify(list).clear();
  inOrder.verifyNoMoreInteractions();</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>将被改变的input mocks</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>和传入参数一样的mocks</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="paragraph">
<p>1.9.0</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_inorder函数">53.34. inOrder函数</h3>
<div class="paragraph">
<p><code>public static InOrder inOrder(Object... mocks)</code></p>
</div>
<div class="paragraph">
<p>创建http://site.mockito.org/mockito/docs/current/org/mockito/InOrder.html[InOrder]对象验证
mocks in order</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">InOrder inOrder = inOrder(firstMock, secondMock);

inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");</code></pre>
</div>
</div>
<div class="paragraph">
<p>验证in
order是很灵活的。你可以只验证你感兴趣的，<strong>并不需要一个一个验证所有的交互</strong>。同样你也可以创建InOrder对象只在相关in-order的验证中进行传值。</p>
</div>
<div class="paragraph">
<p><code>InOrder</code> 验证是’greedy’.你很难每一个都注意到。你可以在Mockito
<a href="https://code.google.com/p/mockito/w/list">wiki
pages</a>页搜索’greedy’获取更多信息。</p>
</div>
<div class="paragraph">
<p>Mockito
1.8.4版本你能以order-sensitive方式调用`verifyNoMoreInvocations()`，阅读更多http://site.mockito.org/mockito/docs/current/org/mockito/InOrder.html#verifyNoMoreInteractions()[InOrder.verifyNoMoreInteractions()]</p>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>in order中被修改的mocks</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>in order中被用于验证的InOrder对象</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_mock函数">53.35. mock函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; T mock(Class &lt;T&gt; classToMock)</code></p>
</div>
<div class="paragraph">
<p>对给定的类或接口创建mock对象</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="paragraph">
<p>需要mock的类或接口</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>mock对象</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_mock函数_2">53.36. mock函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; T mock(Class &lt;T&gt; classToMock, Answer defaultAnswer)</code></p>
</div>
<div class="paragraph">
<p>根据它对交互的回应指明策略创建mock对象。这个是比较高级特性并且你不需要它写多少测试代码。但是对于legacy系统这是非常有用的。</p>
</div>
<div class="paragraph">
<p>这个是默认answer，所以当<strong>你不想测试</strong>函数时你可以使用它。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">Foo mock = mock(Foo.class, RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnAnswer());</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>需要mock的类或接口</p>
</li>
<li>
<p>未测试函数的默认answer</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p>mock对象</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_mock函数_3">53.37. mock函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; T mock(Class &lt;T&gt; classToMock, MockSettings mockSettings)</code></p>
</div>
<div class="paragraph">
<p>没有标准的设置来创建mock对象</p>
</div>
<div class="paragraph">
<p>配置点的数目对mock的扩大有影响，所以我们在没有越来越多重载Mockito.mock()的情况下需要一种更流利的方式来介绍一种新的配置方式。即http://site.mockito.org/mockito/docs/current/org/mockito/MockSettings.html[MockSettings].</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">Listener mock = mock(Listener.class, withSettings()
     .name("firstListner").defaultBehavior(RETURNS_SMART_NULLS));
   );</code></pre>
</div>
</div>
<div class="paragraph">
<p>使用它时需要小心一些并且不要常用。在什么情况下你的测试会不需要标准配置的mocks?在测试代码下太复杂以至于不需要标准配置的mocks?你有没有重构测试代码来让它更加容易测试？</p>
</div>
<div class="paragraph">
<p>也可以参考http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#withSettings()[withSettings()]</p>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>需要mock的类或接口</p>
</li>
<li>
<p>mock配置</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mock对象</p>
</li>
</ul>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_mock">53.38. mock</h3>
<div class="paragraph">
<p>@Deprecated</p>
</div>
<div class="paragraph">
<p>@已过期</p>
</div>
<div class="paragraph">
<p><code>public static &lt;T&gt; T mock(Class &lt;T&gt; classToMock, ReturnValues returnValues)</code></p>
</div>
<div class="paragraph">
<p>已过期，<em>请使用`mock(Foo.class, defaultAnswer)`</em>;</p>
</div>
<div class="paragraph">
<p>已过期，<strong>请使用`mock(Foo.class, defaultAnswer)`</strong>;</p>
</div>
<div class="paragraph">
<p>见http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#mock(java.lang.Class,%20org.mockito.stubbing.Answer)[mock(Class&#44;
Answer)]</p>
</div>
<div class="paragraph">
<p>为什么会过期？为了框架更好的一致性与交互性用Answer替换了ReturnValues。Answer接口很早就存在框架中了并且它有和ReturnValues一样的责任。没有必要维护两个一样的接口。</p>
</div>
<div class="paragraph">
<p>针对它的返回值需要指明策略来创建mock对象。这个是比较高级特性并且你不需要它写多少测试代码。但是对于legacy系统这是非常有用的。</p>
</div>
<div class="paragraph">
<p>明显地，当你不需要测试方法时可以使用这个返回值。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnReturnValues());</code></pre>
</div>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>需要mock的类或接口</p>
</li>
<li>
<p>未测试方法默认返回值</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mock对象</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_mock_2">53.39. mock</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; T mock(Class &lt;T&gt; classToMock, String name)</code></p>
</div>
<div class="paragraph">
<p>指明mock的名字。命名mock在debug的时候非常有用。名字会在所有验证错误中使用。需要注意的是对于使用太多mock或者collaborators的复杂代码命名mock并不能解决问题。<strong>如果你使用了太多的mock</strong>，为了更加容易测试/调试
你需要对其进行重构而不是对mock命名。</p>
</div>
<div class="paragraph">
<p><strong>如果你使用了@Mock注解，意味着你的mock已经有名字了！</strong></p>
</div>
<div class="paragraph">
<p>@Mock使用字段名称作为mock名字http://site.mockito.org/mockito/docs/current/org/mockito/Mock.html[Read
more]</p>
</div>
<div class="paragraph">
<p>参照http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html[Mockito]类的javadoc帮助文档中的例子</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>需要mock的类或接口</p>
</li>
<li>
<p>mock的名字</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mock对象</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_mockingdetails函数">53.40. mockingDetails函数</h3>
<div class="paragraph">
<p><code>public static MockingDetails mockingDetails(Object toInspect)</code></p>
</div>
<div class="paragraph">
<p>对于Mockito的关联信息返回MockingDetails实例可以用于检查某一特定的对象，无论给定的对象是mock还是监视的都可以被找出来。</p>
</div>
<div class="paragraph">
<p>在Mockito以后的版本中MockingDetails可能会扩大并且提供其它有用的有关mock的信息。e.g. invocations,
stubbing info, etc.</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>要检查的对象。允许为空</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="paragraph">
<p><a href="http://site.mockito.org/mockito/docs/current/org/mockito/MockingDetails.html">MockingDetails</a>实例</p>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>1.9.5</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><span class="image"><img src="http://img.blog.csdn.net/20150731162529393" alt="http://img.blog.csdn.net/20150731162529393"></span></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_n_w开头的函数">54. n-w开头的函数</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_never_函数">54.1. never()函数</h3>
<div class="paragraph">
<p><code>public static VerificationMode never()</code></p>
</div>
<div class="paragraph">
<p>相当于`times(0)<code>,可参见 `times(int)</code><br>
验证交互没有发生. 例如:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">verify(mock, never()).someMethod();</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果你想验证`mock<code>之间没有交互，可以使用</code>verifyZeroInteractions(Object...)`
或者 <code>verifyNoMoreInteractions(Object...)</code> 这两个方法</p>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>验证模式</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_only_函数">54.2. only()函数</h3>
<div class="paragraph">
<p><code>public static VerificationMode only()</code></p>
</div>
<div class="paragraph">
<p>如果当前`mock`的方法只被调用一次，则允许被检验。例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   verify(mock, only()).someMethod();
   //上面这行代码是下面这两行代码的简写

   verify(mock).someMethod();
   verifyNoMoreInvocations(mock);</code></pre>
</div>
</div>
<div class="paragraph">
<p>可以参考`verifyNoMoreInteractions(Object...)`方法</p>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>verification mode</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_reset_t_mocks_函数">54.3. reset(T… mocks)函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; void reset(T... mocks)</code></p>
</div>
<div class="paragraph">
<p>聪明的程序员很少会使用这个方法，因为他们知道使用这个方法意味着这个测试写的很low.通常情况下，你不需要重置你的`mocks<code>,你仅仅需要为你的测试方法创建新的</code>mocks`就可以了。</p>
</div>
<div class="paragraph">
<p>你可以考虑写一些简单的、精悍的、聚焦的测试方法来代替`reset()`这个方法。当你在在测试方法的中间部分用到reset()这个方法时，说明你的测试方法太庞大了。</p>
</div>
<div class="paragraph">
<p>请遵循以下关于测试方法的建议：<code>请保证你的测试方法在一个动作中短小、精悍、聚焦</code>。在`mockito`的邮件列表中有很多关于这方面的主题讨论。</p>
</div>
<div class="paragraph">
<p>我们添加`reset()<code>方法的唯一原因是使得注入容器的</code>mocks`得以有效的运行，具体可以参看issue
55 <a href="http://code.google.com/p/mockito/issues/detail?id=55">here</a> or FAQ
<a href="http://code.google.com/p/mockito/wiki/FAQ">here</a></p>
</div>
<div class="paragraph">
<p>不要败坏了你在程序员界的名声，测试方法中间的`reset()`方法是代码中的害群之马（这意味着你的这个测试方法太多）</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   List mock = mock(List.class);
   when(mock.size()).thenReturn(10);
   mock.add(1);

   reset(mock);
   //此时会清除你之前所有的交互以及测试桩</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Type Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>T - mocks的类型</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>被重置的mocks</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_spy_class_classtospy_函数">54.4. spy(Class classToSpy)函数</h3>
<div class="paragraph">
<p><code>@Incubating  public static &lt;T&gt; T spy(Class&lt;T&gt; classToSpy)</code></p>
</div>
<div class="paragraph">
<p>请参考关于类`spy<code>的文档，过渡使用</code>spy`会导致代码变的非常糟糕。</p>
</div>
<div class="paragraph">
<p>相比与原来的`spy<code>（对象），这种方法可以在类的基础上创建一个</code>spy<code>，而不是一个对象。有时你可以很方便基于类创建spy而避免提供一个</code>spy`对象的实例。</p>
</div>
<div class="paragraph">
<p>因为他们不能被实例化，所以这个对于抽象类的监控非常有用。参见`mocksettings.useconstructor()`。</p>
</div>
<div class="paragraph">
<p>例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   SomeAbstract spy = spy(SomeAbstract.class);

   //Robust API, via settings builder:
   //稳定的API，充过builder方式来设置

   OtherAbstract spy = mock(OtherAbstract.class, withSettings()
      .useConstructor().defaultAnswer(CALLS_REAL_METHODS));

   //Mocking a non-static inner abstract class:
   //模拟一个非静态抽象内部类

   InnerAbstract spy = mock(InnerAbstract.class, withSettings()
      .useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Type Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>T - spy的类型</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>spy的类</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>a spy of the provided class</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Since:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>1.10.12</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_stub_t_methodcall_函数">54.5. stub(T methodCall)函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; DeprecatedOngoingStubbing&lt;T&gt; stub(T methodCall)</code></p>
</div>
<div class="paragraph">
<p>对一个方法打桩会返回结果值或者错误异常，例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> stub(mock.someMethod()).toReturn(10);

 //you can use flexible argument matchers, e.g:
 //你可以使用灵活的参数匹配，例如：

 stub(mock.someMethod(anyString())).toReturn(10);

 //setting exception to be thrown:
 //设置抛出的异常

 stub(mock.someMethod("some arg")).toThrow(new RuntimeException());

 //you can stub with different behavior for consecutive method calls.
 // 你可以对不同作用的连续回调的方法打测试桩：
 //Last stubbing (e.g: toReturn("foo")) determines the behavior for further consecutive calls.
 // 最后面的测试桩（例如：返回一个对象："foo"）决定了接下来的回调方法以及它的行为。

 stub(mock.someMethod("some arg"))
  .toThrow(new RuntimeException())
  .toReturn("foo");</code></pre>
</div>
</div>
<div class="paragraph">
<p>一些用户有点混乱、混淆，是因为相比于’stub()’，’when(Object)’更加被推荐</p>
</div>
<div class="literalblock">
<div class="content">
<pre>   //Instead of:
   //替代为：
   stub(mock.count()).toReturn(10);

    //你可以这样做：
   //You can do:
   when(mock.count()).thenReturn(10);</pre>
</div>
</div>
<div class="paragraph">
<p>当对一个返回值为空且抛出异常的方法打测试桩：`doThrow(Throwable)`测试桩会被重写：例如通常测试桩会设置为常用固定设置，但测试方法可以重写它。切记重写测试桩是一种非常不推荐的写法，因为这样做会导致非常多的测试桩。</p>
</div>
<div class="paragraph">
<p>一旦这个方法打过桩，无论这个方法被调用多少次，这个方法会一直返回这个测试桩的值。</p>
</div>
<div class="paragraph">
<p>当你对相同的方法调用相同的参数打测试桩很多次，最后面的测试桩则非常重要</p>
</div>
<div class="paragraph">
<p>尽管我们可以去验证对测试桩的调用，但通常它都是多余的。比如说你对`foo.bar()<code>打测试桩。如果你比较关心的是当某些情况</code>foo.bar()<code>中断了（经常在</code>verify()<code>方法执行之前）,此时会返回什么。如果你的代码不关心是</code>get(0)`会返回什么那么它就不应该被添加测试桩。如果你还不确定？看http://monkeyisland.pl/2008/04/26/asking-and-telling[这里]</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>methodCall - 调用的方法</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>DeprecatedOngoingStubbing 对象是用来设置测试桩的值或者异常的</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_stubvoid_t_mock_函数">54.6. stubVoid(T mock)函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; VoidMethodStubbable&lt;T&gt; stubVoid(T mock)</code></p>
</div>
<div class="paragraph">
<p>已废弃.使用`doThrow(Throwable)`方法代替去打空测试桩</p>
</div>
<div class="literalblock">
<div class="content">
<pre>    //Instead of:
    //替代为：
   stubVoid(mock).toThrow(e).on().someVoidMethod();

   //Please do:
   //请这样做：
   doThrow(e).when(mock).someVoidMethod();</pre>
</div>
</div>
<div class="paragraph">
<p><code>doThrow()`之所以取代了</code>+stubVoid()`方法，是因为它增加了和它的兄弟方法`+doAnswer()`的可读性以及一致性</p>
</div>
<div class="literalblock">
<div class="content">
<pre> stubVoid(mock).toThrow(new RuntimeException()).on().someMethod();

 //you can stub with different behavior for consecutive calls.
 //你可以对不同作用的连续回调的方法打测试桩：
 //Last stubbing (e.g. toReturn()) determines the behavior for further consecutive calls.
 //最后面的测试桩（例如：`toReturn()`）决定了接下来的回调方法以及它的行为。

 stubVoid(mock)
   .toThrow(new RuntimeException())
   .toReturn()
   .on().someMethod();</pre>
</div>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mock - to stub</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>stubbable object that allows stubbing with throwable</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_timesout_long_millis_函数">54.7. timesout(long millis)函数</h3>
<div class="paragraph">
<p><code>public static VerificationWithTimeout timeout(long millis)</code></p>
</div>
<div class="paragraph">
<p>允许验证时使用`timeout<code>。它会在指定的时间后触发你所期望的动作，而不是立即失败，也许这个对并发条件下的测试非常有用。它和</code>after()<code>是有所有不同的，因为</code>after()<code>会等候一个完整的时期，除非最终的测试结果很快就出来了(例如：当</code>never()<code>失败了),
然而当验证通过时，</code>timeout()<code>会快速地停止，当你使用</code>+times(2)`时会产生不同的行为。例如，当先通过然后失败，在这种情况下，`+timeout<code>将会当</code>time(2)<code>通过时迅速通过，然而</code>after()<code>将会一直运行直到</code>times(2)`失败，然后它也一同失败。</p>
</div>
<div class="paragraph">
<p>这个功能看起来应该极少使用，但在多线程的系统的测试中，这是一个很好的方式</p>
</div>
<div class="paragraph">
<p>目前尚未实现按照顺序去验证</p>
</div>
<div class="literalblock">
<div class="content">
<pre>   //passes when someMethod() is called within given time span
   //当`someMethod()`被以时间段的形式调用时通过

   verify(mock, timeout(100)).someMethod();
   //above is an alias to:
   // 上面的是一个别名

   verify(mock, timeout(100).times(1)).someMethod();

   //passes as soon as someMethod() has been called 2 times before the given timeout
   // 在超时之前，`someMethod()`通过了2次调用
   verify(mock, timeout(100).times(2)).someMethod();

   //equivalent: this also passes as soon as someMethod() has been called 2 times before the given timeout
   //这个和上面的写法是等价的，也是在超时之前，`someMethod()`通过了2次调用
   verify(mock, timeout(100).atLeast(2)).someMethod();

   //verifies someMethod() within given time span using given verification mode
   //在一个超时时间段内，用自定义的验证模式去验证`someMethod()`方法
   //useful only if you have your own custom verification modes.
   //只有在你有自己定制的验证模式时才有用

   verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();</pre>
</div>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>millis - - 时间长度（单位：毫秒）</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>验证模式</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_time_int_wantednumberofinvocations_函数">54.8. time(int wantedNumberOfInvocations)函数</h3>
<div class="paragraph">
<p><code>public static VerificationMode times(int wantedNumberOfInvocations)</code></p>
</div>
<div class="paragraph">
<p>允许验证调用方法的精确次数，例如：</p>
</div>
<div class="literalblock">
<div class="content">
<pre>verify(mock, times(2)).someMethod("some arg");
//连续调用该方法两次</pre>
</div>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>wantedNumberOfInvocations - 希望调用的次数</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>验证模式</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_validatemockitousage_函数">54.9. validateMockitoUsage()函数</h3>
<div class="paragraph">
<p><code>public static void validateMockitoUsage()</code></p>
</div>
<div class="paragraph">
<p>首页，无论遇到任何问题，我都鼓励你去阅读`the Mockito<code>问答集：http://groups.google.com/group/mockito,你也可以在</code>mockito`邮件列表提问http://groups.google.com/group/mockito.</p>
</div>
<div class="paragraph">
<p><code>validateMockitoUsage()`会明确地检验</code>+framework+<code>的状态以用来检查</code>+Mockito+`是否有效使用。但是，这个功能是可选的，因为<strong>’Mockito`会使这个用法一直有效</strong>，不过有一个问题请继续读下去。</p>
</div>
<div class="paragraph">
<p>错误示例:</p>
</div>
<div class="literalblock">
<div class="content">
<pre> //Oops, thenReturn() part is missing:
 //当心，`thenReturn()`部分是缺失的

 when(mock.get());

 //Oops, verified method call is inside verify() where it should be on the outside:
 //当心，下面验证方法的调用在`verify()`里面，其实应该在外面

 verify(mock.execute());

 //Oops, missing method to verify:
 //当心，验证缺失方法

 verify(mock);</pre>
</div>
</div>
<div class="paragraph">
<p>如果你错误的使用了`Mockito<code>，这样将会抛出异常，这样你就会知道你的测试是否写的正确。你要清楚当你使用这个框架时，</code>Mockito<code>会接下来的所有时刻开始验证（例如：下一次你验证、打测试桩、调用</code>+mock+<code>等）。尽管在下一次测试中可能会抛出异常，但这个异常消息包含了一个完整栈踪迹路径以及这个错误的位置。此时你可以点击并找到这个</code>+Mockito+`使用错误的地方。</p>
</div>
<div class="paragraph">
<p>有时，你可能想知道这个框架的使用方法。比如，一个用户想将`validateMockitoUsage()<code>放在它的</code>@after<code>方法中，为了能快速地知道它使用</code>Mockito<code>时哪里错了。如果没有这个，它使用这个框架时将不能那么迅速地知道哪里使用错了。另外在</code>@after<code>中使用</code>validateMockitoUsage()<code>比较好的一点是</code>jUnit runner<code>以及</code>Junit rule<code>中的测试方法在有错误时也会失败，然而普通的</code>next-time<code>验证可能会在下一次测试方法中才失败。但是尽管</code>Junit<code>可能对下一次测试报告显示红色，但不要担心，这个异常消息包含了一个完整栈踪迹路径以及这个错误的位置。此时你可以点击并找到这个</code>Mockito`使用错误的地方。</p>
</div>
<div class="paragraph">
<p>同样在runner中：<code>MockitoJUnitRunner and rule</code>：<code>MockitoRule`在每次测试方法之后运行</code>+validateMockitoUsage()+`</p>
</div>
<div class="paragraph">
<p>一定要牢记通常你不需要’validateMockitoUsage()’和框架验证，因为基于`next-time<code>触发的应该已经足够，主要是因为可以点击出错位置查看强大的错误异常消息。但是，如果你已经有足够的测试基础（比如你为所有的测试写有自己的</code>runner<code>或者基类），我将会推荐你使用</code>validateMockitoUsage()<code>，因为对</code>@After`添加一个特别的功能时将是零成本。</p>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
</div>
<div class="sect2">
<h3 id="_verify_t_mock_函数">54.10. verify(T mock)函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; T verify(T mock)</code></p>
</div>
<div class="paragraph">
<p>验证发生的某些行为 等同于`verify(mock, times(1))` 例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   verify(mock).someMethod("some arg");</code></pre>
</div>
</div>
<div class="paragraph">
<p>上面的写法等同于：</p>
</div>
<div class="literalblock">
<div class="content">
<pre>   verify(mock, times(1)).someMethod("some arg");</pre>
</div>
</div>
<div class="paragraph">
<p>参数比较是通过`equals()<code>方法。可参考</code>ArgumentCaptor<code>或者</code>ArgumentMatcher`中关于匹配以及断言参数的方法。</p>
</div>
<div class="paragraph">
<p>尽管我们可以去验证对测试桩的调用，但通常它都是多余的。比如说你对`foo.bar()<code>打测试桩。如果你比较关心的是当某些情况</code>foo.bar()<code>中断了（经常在</code>verify()<code>方法执行之前）,此时会返回什么。如果你的代码不关心是</code>get(0)`会返回什么那么它就不应该被添加测试桩。如果你还不确定？看http://monkeyisland.pl/2008/04/26/asking-and-telling[这里]</p>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mock - 要被验证的 <strong>Returns:</strong></p>
</li>
<li>
<p>mock本身</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_verifynomoreinteractions_object_mocks_函数">54.11. verifyNoMoreInteractions(Object… mocks)函数</h3>
<div class="paragraph">
<p><code>public static void verifyNoMoreInteractions(Object... mocks)</code></p>
</div>
<div class="paragraph">
<p>检查入参的`mocks<code>是否有任何未经验证的交互,你可以在验证你的</code>mocks<code>之后使用这个方法，用以确保你的</code>mocks`没有其它地方会被调用.</p>
</div>
<div class="paragraph">
<p>测试柱的调用也被看成是交互。</p>
</div>
<div class="paragraph">
<p><strong>警告：</strong>一些使用者，倾向于经常使用`verifyNoMoreInteractions()<code>方法做大量经典的、期望-运行-验证的模拟，甚至是在所有的测试方法中使用。</code>verifyNoMoreInteractions()<code>并不被推荐于使用在所有的测试方法中。在交互测试工具中，</code>+verifyNoMoreInteractions()+`是一个很方便的断言。你只能在当它是明确的、相关的时候使用它。滥用它将导致多余的指定、不可维护的测试。你可以在http://monkeyisland.pl/2008/07/12/should-i-worry-about-the-unexpected/[这里]查找更多的文章。</p>
</div>
<div class="paragraph">
<p>这个方法会在测试方法运行之前检查未经验证的调用，例如：在`setUp()<code>,</code>@Before`方法或者构造函数中。考虑到要写出良好优秀的代码，交互只能够在测试方法中。</p>
</div>
<div class="paragraph">
<p>示例：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> //interactions
 //交互
 mock.doSomething();
 mock.doSomethingUnexpected();

 //verification
 //验证
 verify(mock).doSomething();

 //following will fail because 'doSomethingUnexpected()' is unexpected
 //因为'doSomethingUnexpected()'是未被期望的，所以下面将会失败
 verifyNoMoreInteractions(mock);</code></pre>
</div>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mocks - 被验证的</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_verifyzerointeractions_object_mocks_函数">54.12. verifyZeroInteractions(Object… mocks)函数</h3>
<div class="paragraph">
<p><code>public static void verifyZeroInteractions(Object... mocks)</code></p>
</div>
<div class="paragraph">
<p>传进来的mocks之间没有任何交互。</p>
</div>
<div class="literalblock">
<div class="content">
<pre>   verifyZeroInteractions(mockOne, mockTwo);</pre>
</div>
</div>
<div class="paragraph">
<p>这个方法会在测试方法运行之前检查调用，例如：在`setUp()<code>,</code>@Before`方法或者构造函数中。考虑到要写出良好的代码，交互只能够在测试方法中。</p>
</div>
<div class="paragraph">
<p>你也可以参考never()方法 - 这个方法很明确的表达了当前方法的用途.
具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>mocks - 被验证的</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_when_t_methodcall_函数">54.13. when(T methodCall)函数</h3>
<div class="paragraph">
<p><code>public static &lt;T&gt; OngoingStubbing&lt;T&gt; when(T methodCall)</code></p>
</div>
<div class="paragraph">
<p>使测试桩方法生效。当你想让这个`mock<code>能调用特定的方法返回特定的值，那么你就可以使用它。
简而言之：当你调用</code>x<code>方法时会返回</code>y`。</p>
</div>
<div class="paragraph">
<p><code>when()`是继承自已经废弃的方法</code>+stub(Object)+`</p>
</div>
<div class="paragraph">
<p>例如：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java"> when(mock.someMethod()).thenReturn(10);

 //you can use flexible argument matchers, e.g:
 //你可以使用灵活的参数匹配，例如
 when(mock.someMethod(anyString())).thenReturn(10);

 //setting exception to be thrown:
 //设置抛出的异常
 when(mock.someMethod("some arg")).thenThrow(new RuntimeException());

 //you can set different behavior for consecutive method calls.
 //你可以对不同作用的连续回调的方法打测试桩：
 //Last stubbing (e.g: thenReturn("foo")) determines the behavior of further consecutive calls.
 //最后面的测试桩（例如：返回一个对象："foo"）决定了接下来的回调方法以及它的行为。

 when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException())
  .thenReturn("foo");

 //Alternative, shorter version for consecutive stubbing:
 //可以用以下方式替代，比较小版本的连贯测试桩：
 when(mock.someMethod("some arg"))
  .thenReturn("one", "two");
 //is the same as:
 //和下面的方式效果是一样的
 when(mock.someMethod("some arg"))
  .thenReturn("one")
  .thenReturn("two");

 //shorter version for consecutive method calls throwing exceptions:
 //比较小版本的连贯测试桩并且抛出异常：
 when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException(), new NullPointerException();</code></pre>
</div>
</div>
<div class="paragraph">
<p>当你打空方法的测试桩，相关异常可参见：<code>doThrow(Throwable)</code>,`Stubbing`可以被重写：比如：普通的测试桩可以使用固定的设置，但是测试方法能够重写它。切记重写测试桩是一种非常不推荐的写法，因为这样做会导致非常多的测试桩。</p>
</div>
<div class="paragraph">
<p>一旦这个方法打过桩，无论这个方法被调用多少次，这个方法会一直返回这个测试桩的值。</p>
</div>
<div class="paragraph">
<p>当你对相同的方法调用相同的参数打测试桩很多次，最后面的测试桩则非常重要</p>
</div>
<div class="paragraph">
<p>尽管我们可以去验证对测试桩的调用，但通常它都是多余的。比如说你对`foo.bar()<code>打测试桩。如果你比较关心的是当某些情况</code>foo.bar()<code>中断了（经常在</code>verify()<code>方法执行之前）,此时会返回什么。如果你的代码不关心是</code>get(0)`会返回什么那么它就不应该被添加测试桩。如果你还不确定？看http://monkeyisland.pl/2008/04/26/asking-and-telling[这里]</p>
</div>
<div class="paragraph">
<p>具体例子可以参考`Javadoc<code>中的</code>Mockito`类</p>
</div>
<div class="paragraph">
<p><strong>Parameters:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>methodCall - 调用的方法</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>通常是`OngoingStubbing`对象。不要为被返回的对象创建一个引用。</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_withsettings_函数">54.14. withSettings()函数</h3>
<div class="paragraph">
<p><code>public static MockSettings withSettings()</code></p>
</div>
<div class="paragraph">
<p>可以在创建`mock<code>时添加设置。
不要经常去设置它。应该在使用简单的</code>mocks`时写简单的设置。跟着我重复：简单的测试会使整体的代码更简单，更可读、更可维护。如果你不能把测试写的很简单-那么请在测试时重构你的代码。</p>
</div>
<div class="paragraph">
<p>`mock`设置的例子</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java" data-lang="java">   //Creates mock with different default answer &amp; name
   //用不同的默认结果和名字去创建`mock`

   Foo mock = mock(Foo.class, withSettings()
       .defaultAnswer(RETURNS_SMART_NULLS)
       .name("cool mockie"));

   //Creates mock with different default answer, descriptive name and extra interfaces
   ////用不同的默认结果和描述的名称以及额外的接口去创建`mock`
   Foo mock = mock(Foo.class, withSettings()
       .defaultAnswer(RETURNS_SMART_NULLS)
       .name("cool mockie")
       .extraInterfaces(Bar.class));</code></pre>
</div>
</div>
<div class="paragraph">
<p>有两种原因推荐使用`MockSettings<code>.第一种，有需求要增加另外一种</code>mock<code>设置，这样用起来更方便。第二种，能够结合不同的</code>moke<code>设置以减少大量重载</code>moke()`方法。</p>
</div>
<div class="paragraph">
<p>具体可参考`MockSettings<code>文档来学习</code>mock settins`</p>
</div>
<div class="paragraph">
<p><strong>Returns:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>`mock settings`默认实例</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text" style="text-align: center;">
<a href="https://beian.miit.gov.cn">蜀 ICP 备 2024094310 号</a> | <a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=51160302511654">川公网安备 51160302511654 号</a>  | 联系邮箱: zerothreeoneone@foxmail.com | 
更新于 2024-08-22 21:06:55 CST
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"></script>
<script>hljs.initHighlighting()</script>
</body>
</html>